【问题标题】:How to solve problem with too many re-renders in context?如何解决上下文中重新渲染过多的问题?
【发布时间】:2020-11-24 11:29:52
【问题描述】:

每当我在上下文中更新users (array) 中的user (object) - 所有使用users 的组件都会重新渲染。


我尝试过的

我有一个使用上下文值的组件:

const DashboardCardList=()=> {
    const context = useContext(StaticsContext);
    const users = context.users.filter(user=>user.visible);

    return !users
        ? <Loading/>
        : (
            <Container>
                {users.map(user=>
                    <DashboardCard key={user._id} user={user}/>
                )}
            </Container>
        );
};

我的更新函数(更新上下文状态):

const onUserUpdate=(user)=>{
   const index = this.state.users.findIndex(item => item._id === user._id);
   const users = [...this.state.users]
   users[index] = user;
   this.setState({users:users});
}

最终组件:

const DashboardCard=({user})=> {
    console.log("I'm here!", user);
    return ...;
}

问题

为什么它一直重新渲染?是不是因为context
这个要怎么写?

【问题讨论】:

  • 看来你打错了:findIndex(item =&gt; item._id === item._id);
  • 是的,这就是上下文的“问题”。它每次更改时都会渲染每个组件。这就是 redux 或 mobx 变得如此流行的原因。但是,如果您保持上下文足够小或创建多个上下文,则不会引起问题。如果这样做,请尝试使用 redux/mobx 看看是否会改变它。
  • @Domino987 我明白了。它是否仅在您使用某些显式上下文值(如 users)或我使用 useContext 时呈现?可能是React.memo 或类似的东西在这里有用吗? Redux 对于我的项目 atm 来说似乎有点矫枉过正。
  • @Jax-p Reactm.memo 仅适用于 props,不适用于 useContext 挂钩,但有一些方法,阅读更多 here。但是,如果您没有发现任何问题,那么重新渲染就不是问题,尤其是在项目很小的情况下。 Reredners 本身不是问题,只要它不会减慢您的网站速度。也可以在设置最小的地方研究 zustand 或 recoil。
  • 是的,它适用于DashboardCard 级别

标签: javascript reactjs


【解决方案1】:

没有针对上下文消费者的渲染救助 (v17)。

这是一个演示,Consumer 总是会因为他是上下文消费者而重新渲染,即使他不消费任何东西。

import React, { useState, useContext, useMemo } from "react";
import ReactDOM from "react-dom";

// People wonder why the component gets render although the used value didn't change
const Context = React.createContext();

const Provider = ({ children }) => {
  const [counter, setCounter] = useState(0);
  const value = useMemo(() => {
    const count = () => setCounter(p => p + 1);
    return [counter, count];
  }, [counter]);
  return <Context.Provider value={value}>{children}</Context.Provider>;
};

const Consumer = React.memo(() => {
  useContext(Context);
  console.log("rendered");
  return <>Consumer</>;
});

const ContextChanger = () => {
  const [, count] = useContext(Context);
  return <button onClick={count}>Count</button>;
};

const App = () => {
  return (
    <Provider>
      <Consumer />
      <ContextChanger />
    </Provider>
  );
};

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

修复它:

  • 为每个消耗的值使用一个上下文。这意味着上下文具有单个值,(不,应用程序中的多个上下文没有问题)。
  • 使用状态管理解决方案,如 Recoil.js、Redux、MobX 等(尽管可能有点矫枉过正,但请事先考虑好应用设计)。
  • 可以通过memoizing Provider's values with useMemo实现小优化。

【讨论】:

  • 感谢您的提示。是的,Redux 对于这个来说似乎有点过分了。据我了解您的第一点,我可以为每个用户提供一个单一的上下文,或者为每个具有 reusable 库的用户提供 createStore() - 对吧?问题是上下文不只存储用户。它还存储另一个实体(项目、客户)。它可能有点过于上下文化了,不是吗?
  • 问题在于开发人员将所有内容放在一个上下文中,只需根据您的需要进行拆分即可。在没有真正的性能问题的情况下不要担心重新渲染,典型的应用程序没有 - 大 - 使用 - 甚至足以考虑考虑性能
  • 嗯,但我同时使用所有这些值 - 一直。这就是为什么它们处于相同的上下文中。那么这是否意味着如果我有 40 个用户 - 我有 40 个上下文?性能在这里不是问题。它是 websocket 应用程序,每次重新渲染都在窃取其他用户的关注等。
  • 不知道“窃取其他用户的焦点”是什么意思,但是如果你记住那些愚蠢的组件(不使用上下文的组件),UI 将不会重新呈现,因此用户不应该注意到屏幕上的任何变化。
  • 不,您描述的问题是您有侦听数据和呈现 UI 的组件,它应该分开,获取数据的组件和仅用于查看的组件,这样 UI 就不会闪烁不需要时。
猜你喜欢
  • 1970-01-01
  • 2021-03-07
  • 2022-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-28
  • 2019-11-12
相关资源
最近更新 更多