React项目中Context value太大怎么办

816 阅读2分钟

Context是React项目中跨层级数据传递的API,当然这个API本身消耗很大。假如你有一个AppContext,但是随着项目的复杂,appContextValue变得越来越大,这个时候只要appContextValue中有一个子值发生了改变,则涉及到的子组件都要更新,这对整个项目的性能是极其不利的。这个时候怎么办呢?

方案1(推荐):拆分无关联性的context

​ 如下面的例子,点击两个button中的任何一个,都会导致ThemePageUserPage的重新渲染,这不太合理,因为ThemePageappContextValue.user无关,UserPageappContextValue.theme无关。

export default function App(props) {
  const [appContextValue, setAppContextValue] = useState({
    theme: "red",
    user: "小明",
  });

  let {themem, user} = appContextValue;
  return (
    <div>
      <button
        onClick={() => {
          setAppContextValue({
            ...appContextValue,
            theme: getRandomColor(),
          });
        }}>
        点击修改主题色
      </button>

      <button
        onClick={() => {
          setAppContextValue({
            ...appContextValue,
            user: getRandomName(),
          });
        }}>
        点击修改用户姓名
      </button>

      <AppContext.Provider value={appContextValue}>
        <ThemePage />
        <UserPage />
      </AppContext.Provider>
    </div>
  );
}

是时候做个拆分了,如下:

<AppContext.Provider value={appContextValue}>
   <UserPage />
</AppContext.Provider>

<ThemeContext.Provider value={themeValue}>
   <ExpensiveTree />
</ThemeContext.Provider>

方案2:使用React.memo

如果你因为某些原因不方便使用方案1拆分context,可以考虑使用React.memo:

<AppContext.Provider value={appContextValue}>
  <ThemePage />
  <UserPage user={user} />
</AppContext.Provider>;

这个时候的UserPage改为:

const UserPage = memo(({user}) => {
  console.log("UserPage"); //sy-log 可以通过这个log检查组件是否发生不必要的更新
  return (
    <div className="border">
      <h3>UserPage</h3>
      <p>{user}</p>
    </div>
  );
});

方案3:使用React.useMemo

React.useMemo是一个hook函数,它的执行会返回一个 memoized 值,只有某个依赖项发生变化的时候才重新计算memoized值。原理上则是把这个值记录到了虚拟dom节点上。那么我们就可以使用useMemo记录这个组件,只有依赖项发生改变,才去重新更新组件。

<AppContext.Provider value={appContextValue}>
  <ThemePage />
  {useMemo(
    () => (
      <UserPage user={user} />
    ),
    [user]
  )}
</AppContext.Provider>;

这个时候的UserPage:

const UserPage = ({user}) => {
  console.log("UserPage"); //sy-log 可以通过这个log检查组件是否发生不必要的更新
  return (
    <div className="border">
      <h3>UserPage</h3>
      <p>{user}</p>
    </div>
  );
};

​ 以上三种方案可以用于解决appContextValue过大导致的性能问题,当然,方案1是最理想的,毕竟解决的是根本问题,而方案2和方案3相对曲折了那么一点。最后,再提醒大家一句,Context好用,但是不要滥用哦!

​ 最后,为什么写这个总结呢,因为看到了这个issue,正好最近做项目我的Context value也挺大的,正在寻找解决方案,看到这个文章,虽然我不一定会用这里的方案,但还是值得记录下的。