仅在上下文值更新时有条件地重新渲染
Posted
技术标签:
【中文标题】仅在上下文值更新时有条件地重新渲染【英文标题】:Only conditionally re-render when context value updates 【发布时间】:2021-10-11 12:53:42 【问题描述】:我正在开发一个大型 React 应用程序,其中性能至关重要,不必要的重新渲染代价高昂。
我有以下例子:
const CounterContext = React.createContext();
const CounterProvider = (children) =>
const [counterA, setCounterA] = React.useState(0);
const [counterB, setCounterB] = React.useState(0);
return (
<CounterContext.Provider value=counterA, counterB>
children
<button onClick=() => setCounterA(counterA + 1)>Counter A ++</button>
<button onClick=() => setCounterB(counterB + 1)>Counter B ++</button>
<button onClick=() => setCounterA(0); setCounterB(0)>reset</button>
</CounterContext.Provider>
)
const CounterA = () =>
const value = React.useContext(CounterContext);
console.log('CounterA re-render');
return <p>Counter A: value.counterA</p>;
const CounterB = () =>
const value = React.useContext(CounterContext);
console.log('CounterB re-render');
return <p>Counter B: value.counterB</p>;
;
const App = () =>
return (
<CounterProvider>
<CounterA />
<CounterB />
</CounterProvider>
)
;
jsfiddle:https://jsfiddle.net/mitchkman/k3hm0vfq/1/
单击 CounterA 按钮时,两个 CounterA
和 CounterB
组件将重新渲染。如果value
中的counterA
属性发生变化,我只想重新渲染CounterA
。
我还希望能够为条件重新渲染提供某种形式的灵活性。这是我正在尝试做的伪代码:
const MyComponent = () =>
// Only re-render MyComponent if value.property equals 42
const value = useContext(MyContext, (value) => value.property === 42);
...
;
【问题讨论】:
您想尝试阻止CoutnerA
的任何重新呈现,还是在满足谓词(即counterA === 42
)之前不暴露更新的counterA
状态值? “如果值中的 counterA 属性发生变化,我只想重新渲染 CounterA。”这就是开箱即用的工作方式。如果您不希望 CounterA
由于其他原因重新渲染,请使用 memo
高阶组件并传递自定义 areEqual
函数。
【参考方案1】:
您可能必须将 counterProvider
包装到您的 App
组件中。并使用React.memo
实现仅在该组件的值更改时重新渲染。
我在sandbox这里做过类似的事情
index.js
import StrictMode from "react";
import ReactDOM from "react-dom";
import CounterProvider from "./AppContext";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<CounterProvider>
<App />
</CounterProvider>
</StrictMode>,
rootElement
);
将counterContext
移动到一个单独的文件中,以便您可以导入和包装您的App
组件。
AppContext.js
import React, useCallback from "react";
const CounterContext = React.createContext();
export const CounterProvider = ( children ) =>
const [counterA, setCounterA] = React.useState(0);
const [counterB, setCounterB] = React.useState(0);
const incrementA = () =>
setCounterA(counterA + 1);
;
const incrementB = () =>
setCounterB(counterB + 1);
;
return (
<CounterContext.Provider value= counterA, counterB >
children
<button onClick=incrementA>Counter A ++</button>
<button onClick=incrementB>Counter B ++</button>
<button
onClick=useCallback(() =>
setCounterA(0);
setCounterB(0);
, [])
>
reset
</button>
</CounterContext.Provider>
);
;
export default CounterContext;
将 counterA 和 counterB 值作为 props 传递给 Counter 组件,以便React.memo
可以在重新渲染组件之前检查该值。
App.js
import React from "react";
import CounterContext from "./AppContext";
const CounterA = React.memo(( value ) =>
console.log("CounterA re-render");
return <p>Counter A: value</p>;
);
const CounterB = React.memo(( value ) =>
console.log("CounterB re-render");
return <p>Counter B: value</p>;
);
const App = () =>
const value = React.useContext(CounterContext);
const counterA = value.counterA;
const counterB = value.counterB;
console.log(counterA);
return (
<>
<CounterA value=counterA />
<CounterB value=counterB />
</>
);
;
export default App;
【讨论】:
以上是关于仅在上下文值更新时有条件地重新渲染的主要内容,如果未能解决你的问题,请参考以下文章
React Hooks 有条件地渲染登录/注销 w/本地存储变量
西装外套 |炽热 | RadioGroup \ Radio:更新数据上下文后重新渲染页面元素