ans*_*hul 10 javascript reactjs
这是使用 Context API 时非常常见的性能问题。本质上,每当上下文中的状态值发生变化时,提供者之间包装的整个组件都会重新呈现并导致性能下降。
如果我有这样的包装:
<CounterProvider>
<SayHello />
<ShowResult />
<IncrementCounter />
<DecrementCounter />
</CounterProvider>
Run Code Online (Sandbox Code Playgroud)
价值道具为:
<CounterContext.Provider value={{increment, decrement, counter, hello }} >
{children}
</CounterContext.Provider>
Run Code Online (Sandbox Code Playgroud)
每次我增加组件的计数值时IncrementCounter,整个包装组件集都会重新呈现,因为这就是 Context API 的工作方式。
我做了一些研究并发现了这些解决方案:
React.Memo:我看到很多文章建议使用 React.Memo API,如下所示:<CounterContext.Provider
value={useMemo(
() => ({ increment, decrement, counter, hello }),
[increment, decrement, counter, hello]
)}
>
{children}
</CounterContext.Provider>
Run Code Online (Sandbox Code Playgroud)
然而,这并没有按预期工作。我仍然可以看到所有组件都被重新渲染。使用 Memo API 时我做错了什么?Dan Abramov确实建议在一个开放的 React问题中采用这种方法
如果有人能帮我解决这个问题。谢谢阅读。
You*_*mar 11
“本质上,每当上下文中的状态值发生变化时,提供者之间包装的整个组件都会重新渲染并导致性能下降。”
如果像下面的示例中那样使用上下文,其中组件直接嵌套在提供程序中,则上述语句为 true。当变化时,它们都会重新渲染count,无论它们useContext(counterContext)是否调用过。
const counterContext = React.createContext();
const CounterContextProvider = () => {
const [count, setCount] = React.useState(0);
return (
<counterContext.Provider value={{ count, setCount }}>
<button onClick={() => setCount((prev) => prev + 1)}>Change state</button>
<ComponentOne/>
<ComponentTwo />
</counterContext.Provider>
);
};
const ComponentOne = () => {
console.log("ComponentOne renders");
return <div></div>;
};
const ComponentTwo = () => {
console.log("ComponentTwo renders ");
return <div></div>;
};
function App() {
return (
<CounterContextProvider/>
);
}
ReactDOM.render(
<App />,
document.getElementById("root")
);Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>Run Code Online (Sandbox Code Playgroud)
“本质上,每当上下文中的状态值发生变化时,提供者之间包装的整个组件都会重新渲染并导致性能下降。”
如果您使用childrenprop 来使用嵌套组件,则上述语句是错误的,如下例所示。
这次当count发生变化时,CounterContextProvider会渲染,但由于它的渲染是因为它的状态发生了变化,而不是因为它的父渲染,而且因为组件无法改变它的props,所以 React 不会渲染children。
如果是普通组件的话就是这样。但由于这里涉及到上下文,React 将找到包含useContext(counterContext)并渲染它们的所有组件。
const counterContext = React.createContext();
const CounterContextProvider = ({ children }) => {
const [count, setCount] = React.useState(0);
return (
<counterContext.Provider value={{ count, setCount }}>
<button onClick={() => setCount((prev) => prev + 1)}>Change state</button>
{children}
</counterContext.Provider>
);
};
const ComponentOne = () => {
const { count } = React.useContext(counterContext);
console.log("ComponentOne renders");
return <div></div>;
};
const ComponentTwo = () => {
console.log("ComponentTwo renders ");
return <div></div>;
};
function App() {
return (
<CounterContextProvider>
<ComponentOne />
<ComponentTwo />
</CounterContextProvider>
);
}
ReactDOM.render(
<App />,
document.getElementById("root")
);Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>Run Code Online (Sandbox Code Playgroud)
在上面的示例中,仅在更改ComponentOne时渲染count,这是正常的,因为他正在消耗它。如果上下文的一个值发生更改,则每个调用的组件useContext(counterContext)都会进行渲染。
即使像您那样useMemo包装上下文value对象,只要其依赖项数组中的一个变量发生更改,您就会得到这种行为。
如果这让您感到困扰,建议使用多个简单上下文,而不是使用value具有多个键值对的对象。