据我了解,每当 context值更改时,React的上下文 Provider 都会更新其 Consumers 。
作为提供者后代的所有消费者将重新呈现 供应商的
value
道具发生变化时。从传播 子孙消费者的提供者不受shouldComponentUpdate
方法,因此即使 祖先组件无法进行更新。更改是通过使用 与Object.is相同的算法。
但是,以下代码似乎表明了相反的情况:
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"/>
如图所示,当单击Change OtherState
时:
Provider
的父组件将重新呈现,从而使Provider
看到确实确实存在没有 任何更改为 context值 Toolbox
是PureComponent
Provider
的整体思想是,如果 context值更改,它应该仅更新其Consumers
Object.is
完成更改检查(请参见上文)Consumer
更改时ThemedButton
(OtherState
)仍会更新PureComponent
停止了< / li>
Consumers
才应更新,即使在使用PureComponent
的中间组件中停止重新渲染 PS:通过查看Change OtherState
控制台日志,可以看到单击Object.is
时上下文值不变。
问题
当上下文值没有变化时,为什么ThemedButton
重新呈现?
答案 0 :(得分:1)
useState
每次调用都会返回一个新数组。因此,您将一个新数组传递给每个渲染的上下文。
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"/>