useContext 的使用优化
我们经常会使用Context进行组件间的数据共享,但是使用不注意,也会引入一些组件无效渲染的性能问题。
初始化测试项目
npx create-react-app my-app --template typescript && code my-app
在index.tsx中,移除React.StrictMode组件包裹,直接调用App组件即可:
root.render(
// <React.StrictMode>
<App />,
// </React.StrictMode>
);
为什么要去掉React.StrictMode组件?因为使用这个组件在开发模式下会重复调用生命周期,如果在这个组件内部console.log,会发现输出两次。 这是用于帮助检查生命周期预期之外的副作用。 不过可以放心的是这仅适用于开发模式,在生产模式下不会重复调用生命周期。本文为减少干扰所以将其去除。
常规用法
// 创建context
const MyContext = React.createContext<{ [key: string]: any }>({});
export const ParentComp = React.memo(() => {
console.log("ParentComp, 渲染了");
const [value, setValue] = useState(0);
const setRandomData = useCallback(() => {
setValue(Math.random() * 10000);
}, []);
return (
<MyContext.Provider value={{ value, setRandomData }}>
<div>
ParentComp
<ChildComp1 />
<ChildComp2 />
</div>
</MyContext.Provider>
);
});
// 读取context数据
export const ChildComp1 = React.memo(() => {
console.log("ChildComp1, 渲染了");
const { setRandomData } = useContext(MyContext);
return (
<div>
child1, <button onClick={setRandomData}>按钮</button>
</div>
);
});
export const ChildComp2 = React.memo(() => {
console.log("ChildComp2, 渲染了");
const { value } = useContext(MyContext);
return <div>child2, {value}</div>;
});
结果
点击"按钮"后,浏览器控制台的输出结果:
ParentComp, 渲染了
ChildComp1, 渲染了
ChildComp2, 渲染了
也就是说点击组件ChildComp1的按钮居然会导致ParentComp 、ChildComp1、ChildComp2 都重新渲染了。 但实际上ChildComp1只是使用上下文中的方法,该方法不变所以并不需要重新渲染,这是无效渲染可以优化的。
分析原因
点击触发onClick事件
-> setRandomData触发
—> setValue(setState)
—> ParentComp重新渲染
—> MyContext.Provider的value变更
—> 每一个子组件因为使用useContext(MyContext)都将触发重渲染
既然会触发重渲染,我在业务组件外包一层,让业务组件缓存起来可以吗?