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>
);
}
}