淺談React的Context API

前言

上下文(Context) 提供了在組件之間共享這些值的方法,而沒必要在樹的每一個層級顯式傳遞一個 prop 。javascript

通常狀況下,在你沒有絕對把握用好和必須的場景下,是不推薦使用的。java

可是咱們依然間接的使用着它,好比許多官方依賴在使用,如:react-redux, mobx-react,react-router。咱們須要它功能的時候,更可能是靠第三方依賴庫就能實現,而不是本身手動寫context。可是,依然須要理解它,對用它的一些依賴庫源碼理解頗有幫助。react

import React, { Component } from 'react';

class Grandpa extends Component {
    state = {
        info: 'GrandPa組件的信息'
    }
    render() {
        return (
            <div>
                <Father info={this.state.info}/>
            </div>
        );
    }
}

class Father extends Component {
    render() {
        return (
            <div>
                <Son info={this.props.info}/>
            </div>
        );
    }
}

class Son extends Component {
    render() {
        return (
            <div>
                我是Son組件,拿到信息爲:{this.props.info}
            </div>
        );
    }
}

export default Grandpa;

最後頁面輸出:redux

我是Son組件,拿到信息爲:GrandPa組件的信息

咱們會發現這樣的缺點是一層一層傳值,若是有更多層級和更多數據的話,會讓代碼看起來很不整潔,若是中間哪一個組件忘了傳值基本就完了;並且Father組件也並無用到info值,只是將值傳給Son組件。api

若是使用context,就能幫咱們解決這個層級不停傳值的問題。react-router

context有舊版和新版之分,以React v16.3.0版本劃分。ide

舊版Context API

咱們先來講下舊版context API函數

將代碼改爲以下:this

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class Grandpa extends Component {
    state = {
        info: 'GrandPa組件的信息'
    }
    getChildContext () {
        return { info: this.state.info }
    }
    render() {
        return (
            <div>
                <Father/>
            </div>
        );
    }
}
// 聲明Context對象屬性
Grandpa.childContextTypes  = {
    info: PropTypes.string
}

class Father extends Component {
    render() {
        return (
            <div>
                <Son/>
            </div>
        );
    }
}

class Son extends Component {
    render() {
        return (
            <div>
                我是Son組件,拿到信息爲:{this.context.info}
            </div>
        );
    }
}
// 根據約定好的參數類型聲明
Son.contextTypes  = {
    info: PropTypes.string
}
export default Grandpa;

對PropTypes類型檢查,還能夠寫成下面這種寫法code

class Grandpa extends Component {
    static childContextTypes = {
        info: PropTypes.string
    }
    state = {
        info: 'GrandPa組件的信息'
    }
    getChildContext () {
        return { info: this.state.info }
    }
    render() {
        return (
            <div>
                <Father/>
            </div>
        );
    }
}

在須要傳出去的context值和須要接收context值都要進行類型檢查判斷。

正經常使用這個api可能沒什麼問題,但若是中間哪一個組件用到shouldComponentUpdate方法的話,就極可能出現問題。

三個組件的層級關係以下

<GrandPa>
    <Father>
        <Son/>
    </Father>
</GrandPa>

若是在GrandPa組件設置按鈕點擊能夠更新info的值,即經過this.setState({info: '改變值'})方法
更新 Context:
那麼

  • GrandPa組件經過 setState 設置新的 Context 值同時觸發子組件從新render。
  • Father組件從新render。
  • Son組件從新render,並拿到更新後的Context。

假如在Father組件添加函數

shouldComponentUpdate () {
    return false
}

因爲Father並不依賴任何值,因此咱們默認讓它無需從新render。可是,這會致使Son組件也不會從新render,即沒法獲取到最新的 Context 值。

這樣的不肯定性對於目標組件來講是徹底不可控的,也就是說目標組件沒法保證本身每一次均可以接收到更新後的 Context 值。這是舊版API存在的一個大問題。

而新版API解決了這個問題,咱們來看下新版API怎麼寫的

新版Context API

新版Context 新增了 creactContext() 方法用來建立一個context對象。這個對象包含兩個組件,一個是 Provider(生產者),另外一個是 Consumer(消費者)。
  1. Provider 和 Consumer 必須來自同一個Context對象,即一個由 React.createContext()建立的Context對象。
  2. React.createContext()方法接收一個參數作默認值,當 Consumer 外層沒有對應的 Provider 時就會使用該默認值。
  3. Provider 組件使用Object.is方法判斷prop值是否發生改變,當prop的值改變時,其內部組件樹中對應的 Consumer 組件會接收到新值並從新渲染。此過程不受 shouldComponentUpdete 方法的影響。
  4. Consumer 組件接收一個函數做爲 children prop 並利用該函數的返回值生成組件樹的模式被稱爲 Render Props 模式。

用法

  1. 首先建立一個context對象
React.createContext 方法用於建立一個 Context 對象。該對象包含 Provider 和 Consumer兩個屬性,分別爲兩個 React 組件。
const InfoContext = React.createContext('');
  1. 使用Provider組件生成一個context
class Grandpa extends Component {
    state = {
        info: 'GrandPa組件的信息'
    }
    render() {
        return (
            <InfoContext.Provider value={{ info: this.state.info }}>
                <Father/>
            </InfoContext.Provider>
        );
    }
}
  1. 使用Consumer組件獲取context對象的值
class Son extends Component {
    render() {
        return (
            <InfoContext.Consumer>
            {
                value => {
                    return <div>我是Son組件,拿到信息爲:{value.info}</div>
                }
            }
            </InfoContext.Consumer>
        );
    }
}
相關文章
相關標籤/搜索