React中父组件更新,子组件该如何?
首先看一段代码,对于父组件更新状态的时候,子组件是否会更新:
function Child(props){
console.log('child更新')
const {
childName} = props
return (
<div>
<h5>{
childName}</h5>
<button>changeChildName</button>
</div>
)
}
function Parent(){
const [name,setName] = useState('farther')
const [childName,setChildName] = useState('child')
return (
<div>
<Child childName={
childName} />
<h4>{
name}</h4>
//更新name,父组件自己的状态
<button onClick={
() => {
setName(Math.random())}}>changeFartherName</button>
</div>
)
}
在这个例子中,子组件的childName是从父组件的props中拿到的。但是当我点击按钮,修改的是父组件的状态,props并没有改变。
但是,子组件依然会更新。这是React更新的机制。但是在这种情况下,子组件是没有必要重新render一遍的。
基于上面的情况,memo方法出现了。
React.memo
memo是一个Hoc高阶组件,他可以对组件进行封装,而封装后的组件只有在props变化的时候才会render。
针对于上面的代码,就可以通过memo将子组件进行封装:
function getChild(props) {
//父组件更新自己的状态时,不会render
console.log("child更新");
const {
childName } = props;
return (
<div>
<h5>{
childName}</h5>
<button>changeChildName</button>
</div>
);
}
const Child = memo(getChild);
function Parent() {
const [name, setName] = useState("farther");
const [childName, setChildName] = useState("child");
return (
<div>
<Child childName={
childName} setChildName={
setChildName} />
<h4>{
name}</h4>
<button
onClick={
() => {
setName(Math.random());
}}
>
changeFartherName
</button>
</div>
);
}
通过const Child = memo(getChild);
将Child组件进行包装,这个时候,只有更改childName属性的时候,子组件才会更新。
React.memo同时也提供了第二个参数,接收一个回调函数,参数是新旧的props。例如想控制在什么条件下更新,在什么条件下不更新。通过方法的返回值(true || false)来决定组件的更新。
useCallback
举个例子,有以下场景。当父组件将状态值,和改变状态值的方法都传给了子组件。当父组件更新的时候,子组件从props里拿的方法是重复定义的,还是一直用的一个?
//用来存储父组件传给子组件的setChild方法
let arr = [];
function getChild(props) {
console.log("child更新");
const {
childName, setChild } = props;
arr.push(setChild);
//判断每次传入的setChild方法是不是同一个
if (arr.length > 1) {
console.log(arr[0] === arr[1]); //false 答案不是同一个
}
return (
<div>
<h5>{
childName}</h5>
<button>changeChildName</button>
</div>
);
}
const Child = memo(getChild);
function Parent() {
const [name, setName] = useState("farther");
const [childName, setChildName] = useState("child");
//更改状态值的方法,传递给子组件
const setChild = () => {
if(Math.random() > 2){
setChildName(Math.random())
}
}
return (
<div>
<Child childName={
childName} setChild={
setChild} />
<h4>{
name}</h4>
<button
onClick={
() => {
setChildName(Math.random());
}}
>
changeFartherName
</button>
</div>
);
}
执行上面的代码,可以看出来,每次父组件更新,都要定义一个setChild方法。但是似乎并不需要这种重复定义,所以useCallback出现了。
useCallback接收两个参数,第一个参数是回调函数,第二参数是一个数组。返回一个处理过的回调函数。
只有数组里的依赖项改变的时候,它才会更新。也就是说上面的代码可以经过useCallback处理:
let arr = [];
function getChild(props) {
console.log("child更新");
const {
childName, setChild } = props;
arr.push(setChild);
if (arr.length > 1) {
console.log(arr[0] === arr[1]); //true 说明拿到的方法都是一个方法的引用
}
return (
<div>
<h5>{
childName}</h5>
<button>changeChildName</button>
</div>
);
}
const Child = memo(getChild);
function Parent() {
const [name, setName] = useState("farther");
const [childName, setChildName] = useState("child");
//通过useCallback进行处理
const setChild = useCallback(() => {
if(Math.random() > 2){
setChildName(Math.random())
}
},[])
return (
<div>
<Child childName={
childName} setChild={
setChild} />
<h4>{
name}</h4>
<button
onClick={
() => {
setChildName(Math.random());
}}
>
changeFartherName
</button>
</div>
);
}
而useCallback是useMemo的一个语法糖,对于useMemo的hook这里就不细说了。