一、props 传递数据的烦恼
作为子组件的 Toolbar
必须显式定义 theme
属性, ThemedButton
才能够获的 theme 数据。当 ThemedButton
与 App
之间嵌套了很多层,且使用ThemedButton
的页面又非常多,那么工作就会变的异常麻烦。
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
function Toolbar(props) {
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}
二、Context 解决方案
用 React.createContext 创建的 Context 对象,不论组件嵌套多深,都无需再中间组件显式传递 theme 属性,也可以把 theme 属性值传递下去。
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
三、重要概念说明
1、React.createContext
// 创建Context对象,并设置缺省值(defaultValue)。
const MyContext = React.createContext(defaultValue);
2、Context.Provider
// 给 Context 对象赋值
<MyContext.Provider value={/* 某个值 */}></MyContext.Provider>
3、Class.contextType
class 组件中获取Context对象的数据,分两步:
把 class组件的 contextType 属性 与 Context对象关联起来;
通过
this.context
来获取数据。-
备注:获取数据的过程就是:从父组件一直往上找最近 Context.Provider 的 value 值,找不到就使用 Context 的缺省值。
// 使用方法一 class MyClass extends React.Component { render() { let value = this.context; } } MyClass.contextType = MyContext; // 使用方式二 class MyClass extends React.Component { static contextType = MyContext; render() { let value = this.context; } }
4、Context.Consumer
// 让组件中Context数据,随着Context的变化而自动变化
<MyContext.Consumer>
{value => (<div style={{ backgroundColor: value.background }}></div>)}
</MyContext.Consumer>
5、Context.displayName
// 类型为字符串,是Context在React DevTools显示的名字,方便调试。
const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';
<MyContext.Provider> // "MyDisplayName.Provider" 在 DevTools 中
<MyContext.Consumer> // "MyDisplayName.Consumer" 在 DevTools 中
四、函数组件,使用Context
函数组件中没有 contextType 属性,所以使用 useContext
这个 Hook 函数来解决 props 传递数据的烦恼。
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
// 第一步:创建Context,并设置缺省值
const ThemeContext = React.createContext(themes.light);
function App() {
// 第二步:给Context赋值
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
// 第三步:将Context与函数组件,通过useContext关联
const theme = useContext(ThemeContext);
// 第四步:直接使用
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}