我一直在尝试理解新的React Context API并正在使用它。我只是想检查一个简单的案例 - 当更新提供者的数据时,所有重新呈现的内容。
检查 this small example on Codesandbox
所以,在我的例子中,我有一个App
组件 - 状态是这样的 -
this.state = {
number - A random number
text - A static text
}
我从此处创建了一个新的React Context,其中包含来自州的number
和text
,并将值传递给两个使用者Number
和Text
。
所以我的假设是如果随机数更新,它将改变上下文,并且两个组件都应该触发重新渲染。
但实际上,价值正在更新,但没有重新发生。
所以,我的问题 -
对上下文的更新是否不会通过因使用的rerenders传播?因为当上下文改变时我看不到我的日志/颜色变化。
是否更新了该提供商的所有消费者?
答案 0 :(得分:13)
以下是根据useContext
钩子对您的问题的更新:
const value = useContext(MyContext)
当组件上方最近的
<MyContext.Provider>
更新时,此挂钩将触发重新渲染,并将最新的上下文value
传递给该MyContext
提供者。即使祖先使用React.memo
或shouldComponentUpdate
,从使用useContext
在组件本身开始,仍会发生 renderer 。>组件调用
useContext
将在上下文值更改时始终重新渲染。如果重新渲染组件的成本很高,则可以使用备注对其进行优化。
因此,在下面的代码示例中,组件Number
和Text
将随上下文值的每次更改而重新呈现,因为它们都直接包含useContext(AppContext)
。
const AppContext = React.createContext();
const Number = React.memo(props => {
const renderCount = useRenderCount();
const contextNo = React.useContext(AppContext);
return (
<div style={{ backgroundColor: `${randomColor()}` }}>
Number: rendered {renderCount.current} times.
</div>
);
});
const Text = React.memo(() => {
const renderCount = useRenderCount();
const context = React.useContext(AppContext);
return (
<div style={{ backgroundColor: `${randomColor()}` }}>
Text: rendered {renderCount.current} times. I rerender with context value
changes!
</div>
);
});
const App = () => {
const [ctxVal, setCtxVal] = React.useState(0);
const [prop, setProp] = React.useState(0);
return (
<AppContext.Provider value={ctxVal}>
<Number prop={prop} />
<Text />
<button onClick={() => setCtxVal(ctxVal + 1)}>
Change context value
</button>
<button onClick={() => setProp(prop + 1)}>
Only change prop in Number
</button>
</AppContext.Provider>
);
};
function useRenderCount() {
const renderCount = React.useRef(1);
React.useEffect(() => {
renderCount.current += 1;
});
return renderCount;
}
function randomColor() {
const letters = "0123456789ABCDEF"; let color = "#";
for (let i = 0; i < 6; i++) color += letters[Math.floor(Math.random() * 16)];
return color;
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
答案 1 :(得分:12)
对上下文的更新是否不会通过因使用的rerenders传播? 因为在上下文发生变化时我看不到我的日志/颜色变化。
对上下文值的更新不会触发提供者的所有子项的重新呈现,而只触发从Consumer中呈现的组件,因此在您的情况下,虽然数字组件包含Consumer,但数组件不是&n #39;重新渲染,而不仅仅是消费者中的渲染功能,因此值更改了上下文更新。这样它的性能非常高,因为它不会触发所有孩子的重新渲染。
该提供商的所有消费者是否都已更新?
该提供商的所有消费者都将经历更新周期,但他们是否重新呈现是由反应虚拟DOM比较决定的。您可以在控制台中看到此 sandbox
的演示修改强>
您需要确保组件是作为ContextProvider组件的子项呈现的,并且您将处理程序传递给它而不是将它们呈现为内联并更新ContextProvider的状态,因为这将触发所有组件的重新呈现在ContextProvider
高效使用
App.js
render() {
return (
<AppContext.Provider
value={{ ...this.state, updateNumber: this.updateNumber }}
>
{this.props.children}
</AppContext.Provider>
);
}
index.js
class Data extends React.Component {
render() {
return (
<div>
<h1>Welcome to React</h1>
<Number />
<Text />
<TestComp />
<AppContext.Consumer>
{({ updateNumber }) => (
<button onClick={updateNumber}>Change Number </button>
)}
</AppContext.Consumer>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<App>
<Data />
</App>,
rootElement
);
效果较差
App.js
class App extends Component {
constructor() {
super();
this.state = {
number: Math.random() * 100,
text: "testing context api"
};
}
updateNumber = () => {
const randomNumber = Math.random() * 100;
this.setState({ number: randomNumber });
};
render() {
return (
<AppContext.Provider value={this.state}>
<div>
<h1>Welcome to React</h1>
<Number />
<Text />
<TestComp />
<button onClick={this.updateNumber}>Change Number </button>
</div>
</AppContext.Provider>
);
}
}