父组件更新一定会引起子组件的更新。但是有些更新是并不需要的。
例如:
function App(){
const [counter, setCounter] = useState(1);
return(
<div>
counter is {counter}
<button onClick={() => setCounter(counter + 1)}>+1</button>
<Comp1 />
<Comp2 />
</div>
)
}
Comp1
和Comp2
不依赖任何App
的数据,所以更新是没有必要的。
可以使用React.memo来做到这一点,
const MemoziedComp1 = React.memo(Comp1)
const MemoziedComp2 = React.memo(Comp2)
要注意的是memo实际上和PureComponent一样,也会浅比较props。例如下面这种情况,尽管使用了memo但是组件还是会更新。
function App(){
const [counter, setCounter] = useState(1);
return(
<div>
counter is {counter}
<button onClick={() => setCounter(counter + 1)}>+1</button>
<MemoziedComp1 counter={counter} />
<MemoziedComp2 counter={counter} />
</div>
)
}
可以用给memo传递第二参数来解决
const MemoziedComp1 = React.memo(Comp1, () => false)
const MemoziedComp2 = React.memo(Comp2, () => false)
或者
<MemoziedComp1 counter={useMemo(() => counter, [])} />
<MemoziedComp2 counter={useMemo(() => counter, [])} />
第二个参数和类组件的shouldComponentUpdate
类似,返回一个布尔决定是否要更新。但是我并不推荐第二参数固定返回false, 因为这样没有意义,我这里也只是举个例子。
再看一个例子
function App(){
const [counter, setCounter] = useState(1);
const test = () => {
// do something...
}
return(
<div>
counter is {counter}
<button onClick={() => setCounter(counter + 1)}>+1</button>
<MemoziedComp1 test={test} />
<MemoziedComp2 test={test} />
</div>
)
}
这个情况当counter
更新时虽然没有依赖counter
,但是MemoziedComp1
和MemoziedComp2
依然会被更新。因为函数组件的更新就是将函数组件重新执行一遍就像App(props)
一样。函数每次执行就会生成新的执行器上下文,也就是说更新前的test
和更新后的test
不是同一个test
,所以子组件会更新。针对这种情况我们需要用到useCallback
来将test
缓存。
function App(){
const [counter, setCounter] = useState(1);
const memorizedTest = useCallback(function test() {
// do something...
}, [])
return(
<div>
counter is {counter}
<button onClick={() => setCounter(counter + 1)}>+1</button>
<MemoziedComp1 test={memorizedTest} />
<MemoziedComp2 test={memorizedTest} />
</div>
)
}