今天看了下react官网的context,记录下学习过程和自己对context的理解。下面从为什么要用和怎么用两个方面来解释,并参照官网,列出简单的例子来加深记忆。
单词:
provider:
/prəˈvaɪdə(r)/
提供者consumer:
/kənˈsjuːmə(r)/
消费者
为什么要用Context?:
想想如果没有Context
这个概念之前,我们在组件中自上而下传递数据是通过props
属性来实现的。如果层级不多用props
那还好,但是如果层级多了,并且只是最后一层会用到,那我们是不是每一层都得写一个props
,这样的话代码写起来太繁琐,不够优雅,不容易维护, 还容易出错。所以Context
的出现可以解决这个问题。Context
提供了一个无需为每层组件手动添加props,就能在组件之间进行数据传递的方法。(例如:当前认证的用户、主题、首选语言)。
怎么使用?:
要想知道怎么使用,先把Context
的Api
过一遍。这里简单总结下各个Api
的用法,更详细的还是参照官网
-
React.createContext
:创建一个装上下文的容器(组件),defaultValue
可以设置需要共享的默认数据 -
Context.Provider
:提供者,用于提供共享数据的地方,value
属性设置什么数据就共享什么数据 -
Context.Consumer
:消费者,专门消费Provider
提供的共享数据,Consumer
需要嵌套在Provider
下面,才能通过回调的方式拿到共享的数据。<u>只有函数组件会用到。</u> -
Class.contextType
:记住是用于指定contextType
等于当前的context
,必须指定,才能通过this.context
来访问到共享的数据。<u>只有类组件会用到。</u> -
Context.displayName
:context对象接收一个名为displayName
的属性,类型为字符串。React DevTools
使用该字符串来确定context要显示的内容(暂时还没用到)
接下来是怎么使用,总的来说就是顶层组件提供数据,下面消费组件来消费/读取数据。通过demo一步步来解释:
此demo是通过工具栏中的按钮来切换主题色
- 首先,创建一个
theme-context.js
文件,用于创建一个装上下文的容器。
// theme-context.js
import React from 'react';
export const themes = {
light: {
foreground: '#000',
background: '#eee'
},
dark: {
foreground: '#fff',
background: '#222'
}
}
// Step1: 创建一个装上下文的容器
export const ThemeContext = React.createContext(
themes.dark // defaultValue 默认值
)
- 在顶层组件设置需要共享的数据
// App.js
import React, { Component } from 'react';
import { themes, ThemeContext } from './theme-context.js';
import ToolBar from './component/Toolbar'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
theme: themes.dark
}
}
toggleTheme = () => {
this.setState(state => ({
theme: state.theme === themes.dark ? themes.light : themes.dark
}))
}
render() {
return (
<div>
<ThemeContext.Provider value={this.state.theme}> {/* Step2: 在顶层组件设置需要共享的数据, 到时候在ToolBar里面会用到*/}
<ToolBar changeTheme={this.toggleTheme}/>
</ThemeContext.Provider>
</div>
)
}
}
- 子组件使用(消费)context(类组件和函数式组件对应两种方式)
// ToolBar.js
import React, { Component } from 'react';
import { ThemeContext } from './theme-context.js'
1. 如果子组件是类组件,需要指定contextType等于当前的context(也是两种方式)
export default class ToolBar extendx Component {
// static contextType = ThemeContext // 方式1:在class组件中声明静态属性static
render() {
let theme = this.context;
return (
<div style={{ border: '1px solid #000', background: theme.background }}>
<h2 style={{ color: theme.foreground }}>ToolBar</h2>
<ThemeButton onClick={this.props.changeTheme}>
Change Theme
</ThemeButton>
</div>
)
}
}
ToolBar.contextType = ThemeContext; // 方式2: 在class组件外面指定
-------------------------------------------------------------------------------------
2. 如果子组件是函数式组件,需要用Consumer组件来包裹,通过value拿到数据
export default function ToolBar(props) {
return (
<ThemeContext.Consumer>
{theme => (
<div style={{ border: '1px solid #000', background: theme.background }}>
<h2 style={{ color: theme.foreground }}>ToolBar</h2>
<ThemeButton onClick={props.changeTheme}>
Change Theme
</ThemeButton>
</div>
)}
</ThemeContext.Consumer>
)
}
// ThemeButton.js中要使用共享数据也是同理,这里贴出ThemeButton.js的代码
// ThemeButton.js
import React, { Component } from 'react';
import { ThemeContext } from './theme-context.js';
export default function ThemeButton(props) {
return (
<ThemeContext.Consumer>
{theme => (
<button
{...props}
style={{background: theme.background, color: theme.foreground}}
></button>
)}
</ThemeContext.Consumer>
)
}
注:要看懂context的代码也是按照这些步骤来看会简单得多。