React-Context
复习react中,对以前迷糊的点重新get一下
API
react官方文档的context中存在这么几个api
- React.createContext
- Context.Provider
- Class.contextType
- Context.Consumer
- Context.displayName
Reacr.createContext
创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的
Provider
中读取到当前的 context 值。
const MyContext = React.createContext(defaultValue);
我们可以创建一个看看里面是啥
let theme = {
light:{
title:'光明'
},
dark:{
title:'黑暗'
}
}
let Context = React.createContext(theme.dark)
//打印之后的结果
/*$$typeof: Symbol(react.context)
Consumer: {$$typeof: Symbol(react.context), _context: {…}, _calculateChangedBits: null, …}
Provider: {$$typeof: Symbol(react.provider), _context: {…}}
_calculateChangedBits: null
_currentRenderer: {}
_currentRenderer2: null
_currentValue: {title: "黑暗"}
_currentValue2: {title: "黑暗"}*/
//我们会看见里面包含到了context里面的两个api-》Consumer,Provider
Context.Provider
<MyContext.Provider value={/* 某个值 */}>
每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。
Provider 接收一个
value
属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。
Class.contextType
挂载在 class 上的
contextType
属性会被重赋值为一个由React.createContext()
创建的 Context 对象。这能让你使用this.context
来消费最近 Context 上的那个值。你可以在任何生命周期中访问到它,包括 render 函数中
Class.Consumer
这里,React 组件也可以订阅到 context 变更。这能让你在函数式组件中完成订阅 context。
这需要函数作为子元素(function as a child)这种做法。这个函数接收当前的 context 值,返回一个 React 节点。传递给函数的
value
值等同于往上组件树离这个 context 最近的 Provider 提供的value
值。如果没有对应的 Provider,value
参数等同于传递给createContext()
的defaultValue
。
最后一个我确实不太想写。。。关于react-devtools的一个显示的名字。。
以上我们大概可以get到怎么用了,那我们可以练手了
- 先走一个context
import React from 'react'
let theme = {
light:{
title:'光明'
},
dark:{
title:'黑暗'
}
}
let Context = React.createContext(theme.dark)
export {
theme,
Context
}
- 走一个最外层组件
import React from 'react'
import Third from './Third'
import {theme,Context} from './context.js'
class App extends React.Component {
constructor(props){
super(props)
this.state = {
list:[1,2,3,4],
theme:theme.dark
}
}
changeTheme=()=>{
this.setState({
...this.state,
theme:this.state.theme.title === '光明' ? theme.dark : theme.light
})
}
render() {
return (
<Context.Provider value={this.state}>
<div>第一级组件</div>
<Toolbar></Toolbar>
<button onClick={this.changeTheme}>变大变小变漂亮</button>
</Context.Provider>
)
}
}
class Toolbar extends React.Component{
constructor(){
super()
this.state={
title : test2.dark
}
}
render(){
return (
<>
<div>第二级组件</div>
<Third />
</>
);
}
}
export default App
- 走一个孙子组件
import React, { Component } from 'react'
import { Context} from './context.js'
class Third extends Component {
static contextType = Context
render() {
console.log(this.context)
return (
<>
<div>{this.context.theme.title}</div>
<ul>
{
this.context.list.map((value, key) => {
return (
<li key={key}>{`${key} - ${value} - ${this.context.theme.title}`}</li>
)
})
}
</ul>
<Context.Consumer>
{
(value) => {
console.log(value,item2)
value.list.map((item, key) => {
return (
<li key={key}>{`${key} - ${item} - ${value.theme.title}`}</li>
)
})
}
</Context.Consumer>
</>
)
}
}
export default Third
//这样就能生成一个li列表
0-1-黑暗
1-2-黑暗
2-3-黑暗
3-4-黑暗
//并且点击按钮之后会切换光明和黑暗文字
以上就是context的基本使用,唉其实网上都有差不多的,主要是以下的问题促使我写这篇东西的
1、 createContext
的时候会有一个defaultvalue
,然后再provider
的时候会有一个value
,两者的关系是什么呢?
我们把
creactContex
t中的defaultvalue
删除的时候会发现并不影响程序运行,那么defaultvalue
到底有什么用呢,和provider
上的value有什么联系呢?
只有当组件所处的树中没有匹配到
Provider
时,其defaultValue
参数才会生效。这有助于在不使用 Provider 包装组件的情况下对组件进行测试。注意:将undefined
传递给 Provider 的 value 时,消费组件的defaultValue
不会生效。
以上是官网的解释,ok那就是当我们的
provider
没有value的时候,privider
传递的就是defaultvalue
。
2、多个context
怎么写?
<Context1.Consumer>
{
(value) => {
return <Context2.Consumer>
{
item2 => {
console.log(value,item2)
value.list.map((item, key) => {
return (
<li key={key}>{`${key} - ${item} - ${value.theme.title}`}</li>
)
})
}
}
</Context2.Consumer>
}
}
</Context1.Consumer>
3、contextType
和Consumer
都是可以拿到context
进行组件渲染,那么他们有什么区别?
其实当时我也很迷惑,并且网上我并没有搜到答案,于是当我继续按照文档过多个
context
的时候发现了端倪,在官网上多个context
的例子只是用Consumer
来写的,并没有用到contextType
,并且我自己尝试的时候发现如果渲染多个的话contextType
我是没有想到方法。所以我想区别大概是
contextType
并不能对应对多个context
(主)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>
);
}
}