1 有什么作用
使用 Context 可以在组件树中共享全局数据,如主题、语言等,而无需使用 props 将数据在组件树中层层传递。
2 使用方法
2.1 创建 Context
1 2
| const MyContext = React.createContext(defaultValue);
|
使用 React.createContext 来创建 Context。
- Context 对象被创建后,可以被其他组件订阅
- 订阅 Context 对象的组件被称作
消费组件
- 一个 Context 可以被多个
消费组件
订阅
2.2 使用 Provider 包裹消费组件
Context.Provider 属性是一个组件,使用 Context.Provider 包裹消费组件
,能够将 Context 值的最新变化传递到消费组件
,使消费组件被重新渲染
。
消费组件
从离自身最近的 Provider 中读取当前的 Context 值,没找到 Provider 时,会使用 Context 被创建时的默认值。
- 多个 Provider 可以嵌套使用,里层的会覆盖外层的数据。
- 当 Provider 的 value 值发生变化时,其内部的所有消费组件都会被重新渲染。
- Provider 及其内部
消费组件
不受制于 shouldComponentUpdate 函数,因此消费组件
在其祖先组件跳过更新的情况下也能更新。
1 2
| <MyContext.Provider value={"somevalue"} />
|
2.2 订阅 Context
2.2.1 Context.Consumer
当消组件费
为函数组件时,使用 Context.Consumer 来包裹子组件,可以订阅 Context 对象。
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
| import {ThemeContext} from './theme-context';
function ThemeTogglerButton() { return (
<ThemeContext.Consumer> {({theme, toggleTheme}) => ( <button onClick={toggleTheme} style={{backgroundColor: theme.background}}>
Toggle Theme </button> )} </ThemeContext.Consumer> ); }
export default ThemeTogglerButton;
|
2.2.2 contextType
当消费组件
为类组件时,通过将 Context 对象赋值给 Class.contextType 来订阅 Context 对象。此方法只能订阅单一的 Context 对象。
2.2.3 订阅多个 Context
React 需要每个 Context 对象是一个单独的节点,以确保 Context 快速渲染。
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 40 41 42 43 44 45 46
| const ThemeContext = React.createContext('light');
const UserContext = React.createContext({ name: 'Guest', });
class App extends React.Component { render() { const {signedInUser, theme} = this.props;
return ( <ThemeContext.Provider value={theme}> <UserContext.Provider value={signedInUser}> <Layout /> </UserContext.Provider> </ThemeContext.Provider> ); } }
function Layout() { return ( <div> <Sidebar /> <Content /> </div> ); }
function Content() { return ( <ThemeContext.Consumer> {theme => ( <UserContext.Consumer> {user => ( <ProfilePage user={user} theme={theme} /> )} </UserContext.Consumer> )} </ThemeContext.Consumer> ); }
|
2.3 更新 Context 方法
2.3.1 Provider
可以通过 Provider 的 value 属性来修改 Context 的值,其内部的消费者会被重新渲染。
1 2
| <MyContext.Provider value={"somevalue"} />
|
2.3.2 消费组件
可以通过给 Context 传递一个函数,使得消费组件
可以通过此函数来更新 Context。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
export const ThemeContext = React.createContext({ theme: themes.dark, toggleTheme: () => {} }); export const themes = { light: { foreground: '#000000', background: '#eeeeee', }, dark: { foreground: '#ffffff', background: '#222222', }, };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import {ThemeContext} from './theme-context';
function ThemeTogglerButton() { return ( <ThemeContext.Consumer> {({theme, toggleTheme}) => ( <button onClick={toggleTheme} style={{backgroundColor: theme.background}}> Toggle Theme </button> )} </ThemeContext.Consumer> ); }
export default ThemeTogglerButton;
|
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 40 41 42 43
| import {ThemeContext, themes} from './theme-context'; import ThemeTogglerButton from './theme-toggler-button';
class App extends React.Component { constructor(props) { super(props);
this.toggleTheme = () => { this.setState(state => ({ theme: state.theme === themes.dark ? themes.light : themes.dark, })); };
this.state = { theme: themes.light, toggleTheme: this.toggleTheme, }; }
render() { return ( <ThemeContext.Provider value={this.state}> <Content /> </ThemeContext.Provider> ); } }
function Content() { return ( <div> <ThemeTogglerButton /> </div> ); }
ReactDOM.render(<App />, document.root);
|
2.4 调试
React DevTools 使用 Context.displayName 来确定 context 要显示的内容。
1 2 3 4 5
| const MyContext = React.createContext(/* some value */); MyContext.displayName = 'MyDisplayName';
<MyContext.Provider> // "MyDisplayName.Provider" 在 DevTools 中 <MyContext.Consumer> // "MyDisplayName.Consumer" 在 DevTools 中
|
3 注意
- Context 会影响性能,不要滥用。
- 每个 Provider 提供的数据都是隔离开的,如果需要提供全局的数据,只需在 React 根部组件只提供一个 Provider 即可。
- 为了避免不必要的重复渲染,应使用 state 来配置 Provider 组件的 value 属性。
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
| class App extends React.Component { render() { return ( <Provider value={{something: 'something'}}> <Toolbar /> </Provider> ); } }
class App extends React.Component { constructor(props) { super(props); this.state = { value: {something: 'something'}, }; }
render() { return ( <Provider value={this.state.value}> <Toolbar /> </Provider> ); } }
|