React.js |Context的作用与用法
参照react官方文档,文章用于笔记整理。
Context是什么?
在react应用中,数据总是通过 props 自上而下进行传递。 这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题)。Context 可以共享对于一个组件树而言是“全局”的数据。这样就不必显式地通过组件树的逐层传递 props
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
//传递prop
function Toolbar(props) {
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
//再传递prop
class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}
使用context可以避免通过中间元素传递 props
//1.为 theme 创建一个 context,默认值为light
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
return (
// 2.使用 Provider 传递theme。在这里,将 “dark” 传递下去
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// !中间组件不必指明
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 3.指定 contextType 读取 theme的context对象。
static contextType = ThemeContext;
// 4.React 会往上找到最近的 Provider,然后通过this.context读取Provider的value值。
render() {
return <Button theme={this.context} />;
}
}
Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。谨慎使用,因为这会使得组件的复用性变差
API
React.createContext
创建一个Context对象:
const MyContext = React.createContext(defaultValue);
Context.Provider
Context 对象会返回的一个Provider React 组件:
<MyContext.Provider value={某个值}/>
- Provider 接收一个 value 属性,传递给消费组件(provider React 组件内的组件)
- Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染
- 当消费组件没有匹配到 Provider , Context对象的defaultValue 参数生效(注意:将 undefined 传递给 Provider 的 value 时,defaultValue 不会生效)
- Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据
Class.contextType
如果使用public class fields 语法,可以用 static 初始化contextType。contextType指定了需要读取的Context对象:
class MyClass extends React.Component {
static contextType = MyContext;
render() {
let value = this.context;
}
}
更常用的是Class.contextType。Class.contextType可以对Context对象重赋值:
class MyClass extends React.Component {
//...
render() {
let value = this.context;
}
}
//重赋值
MyClass.contextType = MyContext;
挂载在 class 的 contextType 会被重赋值为一个由 React.createContext() 创建的 Context 对象。使你用 this.context 来访问最近 Context 值。可以在任何生命周期中访问到它
Context.Consumer
基于 context 值渲染React节点
<MyContext.Consumer>
{value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>
这需要函数作为子元素(function as a child)这种做法。函数接收 context 值,返回一个 React 节点。传递给函数的 value 值等于离这个 context 最近的 Provider 提供的 value 值。
Context.displayName
在React DevTools 中对显示的context对象的名称进行更改
const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';
<MyContext.Provider> // 在 DevTools 中会显示"MyDisplayName.Provider"
示例01-动态Context
theme-context.js:储存themes对象和theme的context对象
//1. 声明一个themes对象储存主题 导出
export const themes = {
light: {
background: 'orange',
},
dark: {
background: '#eee',
},
};
//2. 创建context对象 导出
export const ThemeContext = React.createContext(
themes.dark //2.设置默认值dark
);
themed-button.js:创建一个加了主题的按钮组件(ThemedButton)
//1. 引入theme的context对象
import {ThemeContext} from './theme-context';
class ThemedButton extends Component {
render() {
let props = this.props;
return (
<button
{...props}
style={{backgroundColor: this.context.background}} //3. 读取值
/>
);
}
}
//2. context重赋值
ThemedButton.contextType = ThemeContext;
//4. 导出按钮组件
export default ThemedButton;
App.js
//1.引入context对象和themes对象
import {ThemeContext, themes} from './theme-context';
//2.引入按钮组件
import ThemedButton from './themed-button';
// 中间组件 放入按钮,用于切换主题
function Toolbar(props) {
return (
<ThemedButton onClick={props.changeTheme}>
Change Theme
</ThemedButton>
);
}
//父组件
class App extends Component {
constructor(props) {
super(props);
this.state = {
theme: themes.light,
};
//3.定义切换主题的事件
this.toggleTheme = () => {
this.setState(state => ({
theme:state.theme === themes.dark? themes.light:themes.dark,
}));
};
}
render() {
return (
<div>
{/* 4.给Context对象的Provider的value赋值*/}
<ThemeContext.Provider value={this.state.theme}>
{/* 5.prop赋值 传入事件*/}
<Toolbar changeTheme={this.toggleTheme} />
</ThemeContext.Provider>
{/* 6.用于对比,由于没有Provider提供value值,所以会读取context对象的默认值为dark */}
<div>
<Toolbar />
</div>
</div>
);
}
}
export default App
示例02-在嵌套组件中更新 Context
通过 context 传递一个函数,使得消费组件自行更新 context(场景:在嵌套很深的组件中更新 context ):
theme-context.js
export const ThemeContext = React.createContext({
theme: themes.dark,
toggleTheme: () => {},
});
theme-toggler-button.js
import {ThemeContext} from './theme-context';
// 这个按钮组件不仅获取了 theme 值,也从 context 中获取到一个 toggleTheme 函数
function ThemeTogglerButton() {
return (
//Context.Consumer:基于 context 值渲染React节点
<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';
class App extends Component {
constructor(props) {
super(props);
//1.定义事件
this.toggleTheme = () => {
this.setState(state => ({
theme:state.theme === themes.dark? themes.light:themes.dark,
}));
};
//2。定义state
this.state = {
theme: themes.light,
toggleTheme: this.toggleTheme,
};
}
render() {
// 3.给Provider的value赋值,包含了当前的主题和事件
return (
<ThemeContext.Provider value={this.state}>
<ThemeTogglerButton />
</ThemeContext.Provider>
);
}
}
示例03-消费多个 Context
为了确保 context 快速进行重渲染,React 需要使每一个 消费组件的 context 在组件树中成为一个单独的节点。
App.js
import ProfilePage from './ProfilePage'
// 1.创建主题context
const ThemeContext = React.createContext({
bgColor:'#eee',
fontColor:'balck'
});
// 2.创建用户context
const UserContext = React.createContext({
name: 'Guest',
});
class App extends Component {
constructor(props){
super(props)
//3.设置在app组件的想要的状态
this.state = {
theme: {
bgColor:'orange',
fontColor:'white'
},
user: {
name:'Jack'
},
};
}
render() {
const {theme,user}=this.state
//4.给Provider的value传入值
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={user}>
<Content />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
}
function Content() {
//5.现在可以访问到Provider传过来的theme和user
return (
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<ProfilePage user={user} theme={theme} />
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
export default App
ProfilePage.js
export default function ProfilePage (props){
const {user,theme}=props
return(
//给style传入了一个对象
<div style={{display:'inline-block',padding:'5px 8px',backgroundColor:theme.bgColor,color:theme.fontColor}}>
{user.name}
</div>
)
}
可在分支10、11、12中获取示例源码