介绍
在应用开发中,通常会存在一些全局性的内容,例如用户 id
、主题、语言、以及各种 token
。
要保证这些数据的传递,以传统的方式,通常有两种办法:
- 如果直接使用普通的对象,那么
React
就不会帮你自动更新 UI
; - 通过
props
的层层注入,需要修改大量的组件,且容易使得代码难以维护。
通过 上下文(Context) 就可以解决这些问题。
定义上下文
使用 React.createContext(默认值)
就可以定义一个上下文:
1
| const Config = React.createContext({theme: 'light'});
|
默认情况下,上下文在浏览器中显示为 Context
,如果希望修改 React DevTool
显示的名称,可以修改实例的 displayName
属性:
1
| Config.displayName = 'Config';
|
使用上下文
每个 Context
对象都会返回一个 Provider
组件,它允许里面的子组件订阅 context
的变化。
Provider
需要接收一个 value
属性,当其发生变化时,所有子组件将会重新渲染。
对于需要接受数据的类组件,需要定义一个静态属性 contextType
指向Context
对象,然后就可以通过 this.context
使用上下文中的数据了。
我们看一个具体的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const config = { theme: "light" }; const Config = React.createContext(config); function App() { return ( <Config.Provider value={config}> <NavBar /> </Config.Provider> ); }
function NavBar() { return <ThemedButton />; }
class ThemedButton extends React.Component { static contextType = Config; render() { return <button>{this.context.theme}</button>; } }
|
如果组件中的 context
找不到上层的 Provider
,那么 组件中的 context
将使用createContext
提供的默认值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const config = { theme: "light" }; const Config = React.createContext(config); function App() { return ( <React.Fragment> <Config.Provider value={{ theme: "dark" }}> <NavBar /> </Config.Provider> <NavBar /> </React.Fragment> ); }
function NavBar() { return <ThemedButton />; }
class ThemedButton extends React.Component { static contextType = Config; render() { return <button>{this.context.theme}</button>; } }
|
这个案例中,第一个按钮在一个 Provider
下,显示为 dark
,第二个则没有在一个 Provider
下,显示为 light
。
监听状态的变更
每个上下文对象具有一个 Consumer
属性,用来订阅 context
的变更。
它的子元素应该是一个函数,参数为 value
,返回值为React Dom
。
1 2 3
| <Config.Consumer> {value => } </Config.Consumer>
|
实例:修改主题
下面一个例子,演示了如何在嵌套的组件中修改主题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| const config = { theme: "light", setTheme: () => null }; const Config = React.createContext(config); Config.displayName = "Config";
class App extends React.Component { constructor(props) { super(props); this.state = { theme: "dark", }; }
render() { let setTheme = () => this.setState({ theme: this.state.theme === "dark" ? "light" : "dark" }); return ( <React.Fragment> <Config.Provider value={{ theme: this.state.theme, setTheme }}> <NavBar /> </Config.Provider> </React.Fragment> ); } }
function NavBar() { return <ThemedButton />; }
class ThemedButton extends React.Component { static contextType = Config; render() { return ( <button {...this.props} onClick={() => this.context.setTheme()}> {this.context.theme} </button> ); } }
|