Hook 实现原理请参考 一文彻底搞懂react hooks的原理和实现
function C() {
console.log(">>> C: render");
return <div>C</div>;
}
function B() {
console.log(">>> B: render");
return <C />;
}
function App() {
const [arr, setArr] = useState([1, 2, 3]);
const [times, setTimes] = useState(1);
useEffect(() => {
console.log(">>> App: render");
});
useEffect(() => {
setTimeout(() => {
console.log(">>> App: update arr");
setArr([1, 2, 3, 4]);
}, 500);
}, []);
return (
<div>
<B arr={times} />
<br />
arr is: {arr}
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
上面的 demo 没有使用 useMemo 和 React.useMemo,纯函数组件。那么当 500ms 后,更新 arr 状态后,虽然传递给组件 B 的 times 属性没变,但组件 B 也会重新渲染。
控制台输出:
>>> B: render
>>> C: render
>>> App: render
>>> App: update arr
>>> B: render
>>> C: render
>>> App: render
为什么 B 和 C 会输出 2 次“render”相关信息?“
App 状态更新后,对于其子组件以及子组件的子组件(依次类推),react 会进行重新 render,然后进行 Diff DOM 算法比较,再决定是否更新对应的 DOM 结构
关键点就在于,react 需要重新进行 render,才能和之前的 dom 进行比较。所以触发了所有下层组件的渲染。
怎么去解决任意状态更新,都会造成子函数组件的重新执行呢?
使用 React.memo(component, equqlFunction) 来包装子组件,并且可以定义属性比较函数。
当 equqlFunction 返回 true,代表前后组件属性相同,不会重新执行执行函数组件;返回 false,会重新执行。
关于 React.memo():
1、功能和 class 组件的 shouldComponentUpdate 方法类似,自定义属性比较函数,避免组件的重复渲染
2、使用 react.memo()包装的组件,如果不传入 equqlFunction,默认是浅比较。 传入 equqlFunction 函数,来代替 react 的浅比较,自定义 prevProps 和 nextProps 的比较。
代码示例:
假设 A 使用了 B(被 React.memo 包装过),B 使用了 C。
当在 500ms 后,组件 A 中调用 setArr([1, 2, 3]) 更新 arr 时,会与原来的 arr 进行比较。