React的Context的使用方法簡介

溢楊年華發表於2019-07-21

context

定義: Context提供了一種方式,能夠讓資料在元件樹中傳遞,而不必一級一級手動傳遞。

API : createContext(defaultValue?)。

使用方法:

首先要引入createContext

 

import React, { Component, createContext } from 'react';

 

 然後建立一個Context 

const BatteryContext = createContext();

 

然後用BatteryContext.Provider包裹元件並且傳遞屬性值。

<BatteryContext.Provider value={60}>
     <Middle />  //子元件
</BatteryContext.Provider>

 

 為了方便看出效果,將定義一個子元件和一個孫元件。然後不通過子元件,孫元件直接取值

import React, { Component, createContext } from 'react';

const BatteryContext = createContext();

//宣告一個孫元件
class Leaf extends Component {
  render() {
    return (
    
    )
  }
}

//宣告一個子元件
class Middle extends Component {
  render() {
    return <Leaf /> 
  }
}

class App extends Component {
  render(){
    return (
      <BatteryContext.Provider value={60}>
        <Middle />
      </BatteryContext.Provider>
    );
  }
}

export default App;

 

孫元件需要BatteryContext.Consumer來接收值,Consumer裡面不能直接渲染其他元件,而是要宣告一個函式。函式的引數就是context的值。

class Leaf extends Component {
  render() {
    return (
      <BatteryContext.Consumer>
        {
          battery => <h1>Battery : {battery}</h1>
        }
      </BatteryContext.Consumer>
    )
  }
}

 

 效果圖;

 

這樣沒通過Middle元件來傳遞值,但是Leaf元件能通過context來獲得屬性。這就是context的基本用法。

 

context不但能跨層級來傳遞屬性值,還能在屬性值發生變化的時候重渲染Consumer下面的元素,舉個例子:
 
在state中定義battery並賦值
state = {
    battery: 60
  }

 

然後做一個按鈕,每次點選的時候都要battery減一。  程式碼:
render() {
    const { battery } = this.state;
    return (
      <BatteryContext.Provider value={battery}>
        <button
          type="button"
          onClick={() => this.setState({ battery: battery - 1 })}
        >
          減減
        </button>
        <Middle />
      </BatteryContext.Provider>
    );
  }

 

全部程式碼:
import React, { Component, createContext } from 'react';

const BatteryContext = createContext();

//宣告一個孫元件
class Leaf extends Component {
  render() {
    return (
      <BatteryContext.Consumer>
        {
          battery => <h1>Battery : {battery}</h1>
        }
      </BatteryContext.Consumer>
    )
  }
}

//宣告一個子元件
class Middle extends Component {
  render() {
    return <Leaf />
  }
}

class App extends Component {
  state = {
    battery: 60
  }
  render() {
    const { battery } = this.state;
    return (
      <BatteryContext.Provider value={battery}>
        <button
          type="button"
          onClick={() => this.setState({ battery: battery - 1 })}
        >
          減減
        </button>
        <Middle />
      </BatteryContext.Provider>
    );
  }

}

export default App;

 

 效果圖: 

 

這樣每次點選都會使battery得數值發生變化,從而重渲染Consumer下面的元素。

 

如果有多個Context該怎麼做呢?我們在建立一個 Context
const OnLineContext = createContext();

 

如果有多個context變數的話,只需要把Privider巢狀進來即可,順序不重要。接下來宣告online的Provider了。
class App extends Component {
  state = {
    battery: 60,
    online: false
  }
  render() {
    const { battery, online } = this.state;
    return (
      <BatteryContext.Provider value={battery}>
        <OnLineContext.Provider value={online} >
          <button
            type="button"
            onClick={() => this.setState({ battery: battery - 1 })}
          >
            減減
        </button>
          <button
            type="button"
            onClick={() => this.setState({ online: !online })}
          >
            Switch
        </button>
          <Middle />
        </OnLineContext.Provider>
      </BatteryContext.Provider>
    );
  }

 

與Provider類似。Consumer也需要巢狀,順序不重要。只要Consumer需要宣告函式,所以要注意語法。
class Leaf extends Component {
  render() {
    return (
      <BatteryContext.Consumer>
        {
          battery => (
            <OnLineContext.Consumer>
              {
                online => <h1>Battery : {battery} , Online : {online.toString()}</h1>
              }
            </OnLineContext.Consumer>
          )
        }
      </BatteryContext.Consumer>
    )
  }
}

 

 全部程式碼:

import React, { Component, createContext } from 'react';

const BatteryContext = createContext();
const OnLineContext = createContext();

//宣告一個孫元件
class Leaf extends Component {
  render() {
    return (
      //與Provider類似。Consumer也需要巢狀,順序不重要。只要Consumer需要宣告函式,所以要注意語法。
      <BatteryContext.Consumer>
        {
          battery => (
            <OnLineContext.Consumer>
              {
                online => <h1>Battery : {battery} , Online : {online.toString()}</h1>
              }
            </OnLineContext.Consumer>
          )
        }
      </BatteryContext.Consumer>
    )
  }
}

//宣告一個子元件
class Middle extends Component {
  render() {
    return <Leaf />
  }
}

class App extends Component {
  state = {
    battery: 60,
    online: false
  }
  render() {
    const { battery, online } = this.state;
    //接下來宣告online的Provider了。如果有多個context變數的話,只需要把Privider巢狀進來即可,順序不重要。
    return (
      <BatteryContext.Provider value={battery}>
        <OnLineContext.Provider value={online} >
          <button
            type="button"
            onClick={() => this.setState({ battery: battery - 1 })}
          >
            減減
        </button>
          <button
            type="button"
            onClick={() => this.setState({ online: !online })}
          >
            Switch
        </button>
          <Middle />
        </OnLineContext.Provider>
      </BatteryContext.Provider>
    );
  }

}

export default App;

 

效果圖:

 

 

還有一個問題 , 如果Consumer向上找不到對應的Provider怎麼辦?
 
其實即使找不到也不會報錯,而是顯示為空。那怎麼設定預設值呢?
 
那上面的demo舉例 ,剛才我們設定的battery為60。如果Consumer向上找不到BatteryContext.Provider的值,我們可以這樣設定預設值:
const BatteryContext = createContext(30);
這樣BatteryContext.Consumer向上找不到值,就會取預設值30。

 

context不僅僅只是可以傳數值,也可以傳函式。大家可以試試看。
最後再提示一下大家,不要濫用context,不然會影響元件的獨立性。 如果一個元件中只使用一個Context的話,就可以使用contextType代替Consumer。詳見https://www.cnblogs.com/littleSpill/p/11221817.html
 
 
 

 

相關文章