【发布时间】:2019-03-28 16:54:21
【问题描述】:
据我了解,React 的 Context Provider 会在 context 值 更改时更新其 Consumers。
作为 Provider 后代的所有消费者都将重新渲染 每当 Provider 的
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看到确实没有对任何更改上下文值 - 孩子们将重新渲染,因为他们的父母这样做了,但该过程在过程中途停止,
Toolbox成为PureComponent - 现在,上下文
Provider的整个想法是,如果上下文值发生变化,它应该仅更新其Consumers - 更改检查是使用
Object.is完成的,如文档中所述(见上文) - 尽管如此,
Consumer(ThemedButton) 仍会在OtherState更改时更新 - 这不应该发生,因为 上下文值 实际上 没有 改变,并且子重新渲染在中间停止并带有
PureComponent - 仅当 上下文值 更改时,
Consumers才会更新,即使在带有PureComponent的中间组件中停止重新渲染也是如此
PS:通过查看Object.is 控制台日志,您可以看到单击Change OtherState 时上下文值 没有改变。
问题
为什么ThemedButton 重新渲染,而上下文值 没有改变?
【问题讨论】:
标签: javascript reactjs react-hooks