React-Context

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到怎麼用了,那我們可以練手了

  1. 先走一個context
import React from 'react'
let theme = {
    light:{
        title:'光明'
    },
    dark:{
        title:'黑暗'
    }
}
let Context = React.createContext(theme.dark)
export {
    theme,
    Context
}
  1. 走一個最外層組件
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
  1. 走一個孫子組件
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,兩者的關係是什麼呢?

我們把creactContext中的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、contextTypeConsumer都是可以拿到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>
    );
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章