[英]React: When Does Context Provider Update its Consumers?
To my understanding, React's Context Provider updates its Consumers whenever the context value changes.据我了解,每当上下文值发生变化时,React 的上下文提供者都会更新其消费者。
All consumers that are descendants of a Provider will re-render whenever the Provider's
value
prop changes.每当 Provider 的
value
属性发生变化时,所有作为 Provider 后代的消费者都会重新渲染。 The propagation from Provider to its descendant consumers is not subject to theshouldComponentUpdate
method, so the consumer is updated even when an ancestor component bails out of the update.从 Provider 到其后代消费者的传播不受
shouldComponentUpdate
方法的影响,因此即使祖先组件退出更新,消费者也会更新。Changes are determined by comparing the new and old values using the same algorithm as Object.is .
更改是通过使用与Object.is相同的算法比较新旧值来确定的。
However, the following code seems to indicate the opposite:但是,以下代码似乎表明相反:
var themes = { light: { name: "Light", foreground: "#000000", background: "#eeeeee" }, dark: { name: "Dark", foreground: "#ffffff", background: "#222222" } }; const ThemeContext = React.createContext({ theme: themes.light, updateTheme: () => {} }); let prevTheme = undefined; function App() { console.log("RE-RENDERING App..."); const stateArray = React.useState(themes.light); const [theme, setTheme] = stateArray; const [otherState, setOtherState] = React.useState(true); function handleSetOtherState() { console.log("SETTING OTHER STATE....."); setOtherState(prevState => !prevState); } console.log("theme:", theme); console.log("prevTheme:", prevTheme); console.log(`Object.is(prevTheme, theme): ${Object.is(prevTheme, theme)}`); prevTheme = theme; return ( <ThemeContext.Provider value={stateArray}> <Toolbar /> <button onClick={handleSetOtherState}>Change OtherState</button> </ThemeContext.Provider> ); } class Toolbar extends React.PureComponent { render() { console.log("RE-RENDERING Toolbar (DOES NOT HAPPEN WHEN CHANGING OTHERSTATE)..."); return ( <div> <ThemedButton /> </div> ); } } function ThemedButton() { console.log("RE-RENDERING ThemedButton (SHOULD NOT HAPPEN WHEN CHANGING OTHERSTATE)..."); const themeContext = React.useContext(ThemeContext); const [theme, setTheme] = themeContext; console.log("themeContext:", themeContext); console.log("theme.name:", theme.name); console.log("setTheme:", setTheme); function handleToggleTheme() { console.log("SETTING THEME STATE....."); setTheme( prevState => themes.dark ); } return <button onClick={handleToggleTheme}>Click me: {theme.name}</button>; } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script> <div id="root"/>
As seen, when clicking Change OtherState
:如图所示,当点击
Change OtherState
:
Provider
will re-render, thus allowing the Provider
to see that there has indeed not been any change to the context valueProvider
的父组件将重新渲染,从而允许Provider
看到上下文值确实没有任何更改Toolbox
being a PureComponent
Toolbox
是PureComponent
,该过程在过程PureComponent
Provider
, is that it should only update its Consumers
if the context value changedProvider
的整个想法是,如果上下文值发生变化,它应该只更新其Consumers
Object.is
, as specified in the Docs (see above)Object.is
完成的,如文档中所述(见上文)Consumer
( ThemedButton
) still updates when OtherState
changesOtherState
更改时, Consumer
( ThemedButton
) 仍会更新PureComponent
PureComponent
在中间停止了子重新渲染Consumers
update, even if the re-rendering is stopped in intermediate components with PureComponent
Consumers
应更新,即使在使用PureComponent
中间组件中停止重新渲染PS: You can see that context value does not change when Change OtherState
is clicked, by looking at the Object.is
console log. PS:通过查看
Object.is
控制台日志,您可以看到单击Change OtherState
时上下文值不会更改。
Question题
Why does ThemedButton
re-render, when the context value did not change?当上下文值没有改变时,为什么
ThemedButton
重新渲染?
useState
returns a new array every call. useState
每次调用useState
返回一个新数组。 So you pass a new array to context every render.因此,您将一个新数组传递给每次渲染的上下文。 useMemo to fix the issue.
useMemo来解决这个问题。
var themes = { light: { name: "Light", foreground: "#000000", background: "#eeeeee" }, dark: { name: "Dark", foreground: "#ffffff", background: "#222222" } }; const ThemeContext = React.createContext({ theme: themes.light, updateTheme: () => {} }); let prevTheme = undefined; let prevStateArray = undefined; function App() { console.log("RE-RENDERING App..."); const stateArray = React.useState(themes.light); console.log('stateArray', prevStateArray, stateArray, Object.is(prevStateArray, stateArray)); prevStateArray = stateArray; const [theme, setTheme] = stateArray; const memoState = React.useMemo(() => [theme, setTheme], [theme, setTheme]); const [otherState, setOtherState] = React.useState(true); function handleSetOtherState() { console.log("SETTING OTHER STATE....."); setOtherState(prevState => !prevState); } console.log("theme:", theme); console.log("prevTheme:", prevTheme); console.log(`Object.is(prevTheme, theme): ${Object.is(prevTheme, theme)}`); prevTheme = theme; return ( <ThemeContext.Provider value={memoState}> <Toolbar /> <button onClick={handleSetOtherState}>Change OtherState</button> </ThemeContext.Provider> ); } class Toolbar extends React.PureComponent { render() { console.log("RE-RENDERING Toolbar (DOES NOT HAPPEN WHEN CHANGING OTHERSTATE)..."); return ( <div> <ThemedButton /> </div> ); } } function ThemedButton() { console.log("RE-RENDERING ThemedButton (SHOULD NOT HAPPEN WHEN CHANGING OTHERSTATE)..."); const themeContext = React.useContext(ThemeContext); const [theme, setTheme] = themeContext; console.log("themeContext:", themeContext); console.log("theme.name:", theme.name); console.log("setTheme:", setTheme); function handleToggleTheme() { console.log("SETTING THEME STATE....."); setTheme( prevState => themes.dark ); } return <button onClick={handleToggleTheme}>Click me: {theme.name}</button>; } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script> <div id="root"/>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.