Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
数据流向下传递,由最高层级的组件注入数据,在任一子组件进行接收。
使用 context, 我们可以避免通过中间元素传递 props
**如果你只是想避免层层传递一些属性,组件组合(component composition)有时候是一个比 context 更好的解决方案。**使用场景不多,这里不再做过多的赘述,有兴趣的可以去官网看看
中文官网地址:react.docschina.org/docs/contex…
例如:
1.首先在父组件中(father.js),我们进行数据的注入:
1)引入createContext函数
import React, {createContext } from 'react'
2)在父组件中把创建的 createContext暴露出去
createContxt(value),value是默认值
export const ThemeContext = createContext('light')
3)在父组件的render函数中进行注入:value={this.state}变量或静态值也可
使用 Context.Provider 来进行数据注入
<ThemeContext.Provider value="dark">
<Child />
</ThemeContext.Provider>
2.在任一后代组件树中(Child )进行数据的接收:
2.1 使用 **Context.**Consumer来进行接收
1)首先引入父组件暴露的context函数
import { ThemeContext } from './father'
2)在子组件中进行渲染,ThemeContext.Consumer
<ThemeContext.Consumer>
{(value) => (
<h1 style={{ color: 'green' }}>
任一子组件接受的值Context : {value}
</h1>
)}
</ThemeContext.Consumer>
2.2 使用contextType来接收
class Child extends React.Component {
static contextType = MyContext;
render() {
let value = this.context;
/* 基于这个值进行渲染工作 */
}
}
3.在嵌套组件中更新 Context
从一个在组件树中嵌套很深的组件中更新 context 是很有必要的。在这种场景下,你可以通过 context 传递一个函数,使得 consumers 组件更新 context:
//----------首先创建createContext并暴露出去
//theme-context.js
export const ThemeContext = React.createContext({
theme: themes.dark,
toggleTheme: () => {}
});
//----------建立一个theme-toggler-button的子组件进行值得接收
//theme-toggler-button.js
import {ThemeContext} from './theme-context';
function ThemeTogglerButton() {
// Theme Toggler 按钮不仅仅只获取 theme 值,它也从 context 中获取到一个 toggleTheme 函数 return ( <ThemeContext.Consumer>
{({theme, toggleTheme}) => (
<button
onClick={toggleTheme}
style={{backgroundColor: theme.background}}>
Toggle Theme
</button>
)}
</ThemeContext.Consumer>
);
}
export default ThemeTogglerButton;
//----------app.js
//在父组件进行值得注入
import {ThemeContext, themes} from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';
export default class App extends React.Component { constructor(props) {
super(props);
//toggleTheme的回调函数 注入到子组件
this.toggleTheme = () => {
this.setState(state => ({
theme:
state.theme === themes.dark
? themes.light
: themes.dark,
}));
};
// State 也包含了更新函数,因此它会被传递进 context provider
this.state = {
theme: themes.light,
toggleTheme: this.toggleTheme,
}}
render() {
// 整个 state 都被传递进 provider
return (
<ThemeContext.Provider value={this.state}>
<ThemeTogglerButton />
</ThemeContext.Provider>
);
}
}
4.消费多个 Context
为了确保 context 快速进行重渲染,React 需要使每一个 consumers 组件的 context 在组件树中成为一个单独的节点
-----------父组件进行数据的注入
// Theme context,默认的 theme 是 “light” 值
const ThemeContext = React.createContext('light');
// 用户登录 context
const UserContext = React.createContext({
name: 'Guest',
});
class App extends React.Component {
render() {
const {signedInUser, theme} = this.props;
return (
//多个嵌套的 Context
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={signedInUser}>
<Layout />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
}
//------------子组件进行数据的接收
function Content() {
// 一个组件可能会消费多个 context
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => ( <ProfilePage user={user} theme={theme} /> )} </UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
注意事项
因为 context 会使用参考标识(reference identity)来决定何时进行渲染,这里可能会有一些陷阱,当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例子,当每一次 Provider 重渲染时,以下的代码会重渲染所有下面的 consumers 组件,因为 value 属性总是被赋值为新的对象
class App extends React.Component {
render() {
return (
<MyContext.Provider value={{something: 'something'}}>
<Toolbar />
</MyContext.Provider>
);
}
}
为了防止这种情况,将 value 状态提升到父节点的 state 里:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
render() {
return (
<Provider value={this.state.value}>
<Toolbar />
</Provider>
);
}
}