React中的context的用法和使用場景和發布-訂閱模式


使用場景

如果你在組件間傳遞的數據邏輯比較復雜,可以使用redux;

如果組件層級不多,可以使用props;

如果層級較深,數據邏輯簡單,可以使用context或者發布-訂閱模式。

 

 在 React 16.3 之前,Context API 由於存在種種局限性,並不被 React 官方提倡使用,開發者更多的是把它作為一個概念來探討。而從 v 16.3.0 開始,React 對 Context API 進行了改進,新的 Context API 具備更強的可用性。

 

以下是代碼演示:

1.創建一個Context,const ThemeContext = React.createContext('light')

2.在父組件中用ThemeContext.Provider包裹子組件,用value傳遞數據

3.如果子組件是函數組件,要使用context時,需要用ThemeContext.Consumer包裹,通過value拿到數據;如果子組件是類組件,要使用context時,需要指定 contextType 讀取當前的 theme context,這有兩種方式,一種是在類組件中聲明靜態屬性static contextType = ThemeContext,另一種是在組件外定義ThemedButton.contextType = ThemeContext,通過this.context拿到數據

import React from 'react'

// 創建 Context 填入默認值(任何一個 js 變量)
const ThemeContext = React.createContext('light')
let { Consumer, Provider } = ThemeContext;

// 底層組件 - 函數是組件
function ThemeLink(props) {
    // const theme = this.context // 會報錯。函數式組件沒有實例,即沒有 this

    // 函數式組件可以使用 Consumer
    return <Consumer>
        {value => <p>link's theme is {value}</p>}
    </Consumer>
}

// 底層組件 - class 組件
class ThemedButton extends React.Component {
    // 指定 contextType 讀取當前的 theme context。
    // static contextType = ThemeContext // 也可以用 ThemedButton.contextType = ThemeContext
    render() {
        const theme = this.context // React 會往上找到最近的 theme Provider,然后使用它的值。
        return <div>
            <p>button's theme is {theme}</p>
        </div>
    }
}
ThemedButton.contextType = ThemeContext // 指定 contextType 讀取當前的 theme context。

// 中間的組件再也不必指明往下傳遞 theme 了。
function Toolbar(props) {
    return (
        <div>
            <ThemedButton />
            <ThemeLink />
        </div>
    )
}

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'light'
        }
    }
    render() {
        // 用Provider包起來
        return <Provider value={this.state.theme}>
            <Toolbar />
            <hr />
            <button onClick={this.changeTheme}>change theme</button>
        </Provider>
    }
    changeTheme = () => {
        this.setState({
            theme: this.state.theme === 'light' ? 'dark' : 'light'
        })
    }
}

export default App

 

發布-訂閱模式

class myEventEmitter {
  constructor() {
    // eventMap 用來存儲事件和監聽函數之間的關系
    this.eventMap = {};
  }
  // type 這里就代表事件的名稱
  on(type, handler) {
    // hanlder 必須是一個函數,如果不是直接報錯
    if (!(handler instanceof Function)) {
      throw new Error("哥 你錯了 請傳一個函數");
    }
    // 判斷 type 事件對應的隊列是否存在
    if (!this.eventMap[type]) {
      // 若不存在,新建該隊列
      this.eventMap[type] = [];
    }
    // 若存在,直接往隊列里推入 handler
    this.eventMap[type].push(handler);
  }
  // 別忘了我們前面說過觸發時是可以攜帶數據的,params 就是數據的載體
  emit(type, params) {
    // 假設該事件是有訂閱的(對應的事件隊列存在)
    if (this.eventMap[type]) {
      // 將事件隊列里的 handler 依次執行出隊
      this.eventMap[type].forEach((handler, index) => {
        // 注意別忘了讀取 params
        handler(params);
      });
    }
  }
  off(type, handler) {
    if (this.eventMap[type]) {
        // >>> 無符號位移 自然數(大於等於0的整數)>>>0 還是該自然數
        // -1 >>> 0 =4294967295 對數組沒有影響
        // 關於位移相關內容,查看二進制和位移知識篇
      this.eventMap[type].splice(this.eventMap[type].indexOf(handler) >>> 0, 1);
    }
  }
}

 

待續。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM