從編程中的context來講react - context(16.x

參考資料:javascript

  1. 編程中什麼是「Context(上下文)」?.
  2. react - context官方文檔.
  3. React Context(上下文) 做用和使用 看完不懂 你打我.
  4. React - Context 源碼 github.

編程上的context

what is context ?

在這裏插入圖片描述

這是牛津詞典裏有關 context 的解釋。一般咱們在編程當中會把 context 叫作 上下文,不過我認爲這裏彷佛叫作 語境更合適一些。可是不論是叫作 上下文或是 語境,都仍是有些抽象。html

從語文的角度理解

在學生時代,語文試卷中都會有個閱讀理解部分,一般會有個問題相似於「結合上下文,x x x x x x x」,例如:java

..... 林沖大叫一聲「啊也!」 ..... 問:這句話林沖的「啊也」表達了林沖怎樣的內心? 答:啊你媽個頭啊!react

看,一篇文章,給你摘錄一段,沒前沒後,你讀不懂。由於有 語境,就是語言環境存在,一段話說了什麼,要經過上下文(文章的上下文)來推斷。git

從編程的角度理解

那麼從編程的角度又該如何理解呢?一般咱們寫代碼的時候,除了一些簡單的函數不須要外部變量,其餘的複雜些的函數大多會依賴一些外部變量。而一旦須要外部變量,那麼這段代碼就不是完整的,不是可以獨立運行的,而這裏這些程序運行所必需的外部變量(也能夠理解爲前置條件),就叫作 上下文github

react - context

首先,react 在 16.3 引入了context,通過不過的迭代,到如今16.8.6的版本中, 官方對context的定義爲 --- Context 提供了一個無需爲每層組件手動添加 props,就能在組件樹間進行數據傳遞的方法。歸類於高級指引部分,屬於react的高級api。web

什麼時候使用context

一般在咱們寫react組件的時候,數據傳遞通常採用單相數據流的方式,這樣可使數據流向變得的簡單而清晰。但在某些使用場景中須要共享一些對於一個組件樹而言是**「全局」**的數據,這時就須要在每個層級手動修改傳輸的props,過於麻煩。 例如官方文檔中提供的demo:編程

class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}

function Toolbar(props) {
  // Toolbar 組件接受一個額外的「theme」屬性,而後傳遞給 ThemedButton 組件。
  // 若是應用中每個單獨的按鈕都須要知道 theme 的值,這會是件很麻煩的事,
  // 由於必須將這個值層層傳遞全部組件。
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}

class ThemedButton extends React.Component {
  render() {
    return <Button theme={this.props.theme} />;
  }
}
複製代碼

在上述代碼中,修改Button的theme,那麼須要從App中修改theme,傳給Toolbar,並由Toolbar傳給ThemedButton使用,如此層層傳遞。若是ThemedButton與App以前還存在更多的嵌套關係,那麼必需將theme在中間的各層級間層層傳遞,顯然,這樣過於繁瑣了。redux

使用 context, 咱們能夠避免經過中間元素傳遞 props

// Context 可讓咱們無須明確地傳遍每個組件,就能將值深刻傳遞進組件樹。
// 爲當前的 theme 建立一個 context(「light」爲默認值)。
const ThemeContext = React.createContext('light');

class App extends React.Component {
  render() {
    // 使用一個 Provider 來將當前的 theme 傳遞給如下的組件樹。
    // 不管多深,任何組件都能讀取這個值。
    // 在這個例子中,咱們將 「dark」 做爲當前的值傳遞下去。
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

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

class ThemedButton extends React.Component {
  // 指定 contextType 讀取當前的 theme context。
  // React 會往上找到最近的 theme Provider,而後使用它的值。
  // 在這個例子中,當前的 theme 值爲 「dark」。
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}
複製代碼

這是16.8.6版本中的文檔demo(本文不對16.x以前的context使用方式作探討,有興趣同窗可自行研究)api

API

  1. 首先使用createContext方法建立一個Context對象。包含ProviderConsumer兩個組件。當 React 渲染一個訂閱了這個 Context 對象的組件,這個組件會從組件樹中離自身最近的那個匹配的 Provider 中讀取到當前的 context 值。
const ThemeContext = React.createContext('light');
複製代碼
  1. 使用**Provider(生產者)**組件來傳遞theme,圈定組件樹範圍,不管其內嵌套多深,任何組件都能讀取theme。每一個 Context 對象都會返回一個 Provider React 組件,它容許消費組件訂閱 context 的變化。
class App extends React.Component {
  render() {
    // 使用一個 Provider 來將當前的 theme 傳遞給如下的組件樹。
    // 不管多深,任何組件都能讀取這個值。
    // 在這個例子中,咱們將 「dark」 做爲當前的值傳遞下去。
    return (
      <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); } } 複製代碼
  1. 當子孫組件須要使用theme時,指定contextType使用當前的Context
class ThemedButton extends React.Component {
  // 指定 contextType 讀取當前的 theme context。
  // React 會往上找到最近的 theme Provider,而後使用它的值。
  // 在這個例子中,當前的 theme 值爲 「dark」。
  static contextType = ThemeContext;
  render() {
  // 掛載在 class 上的 contextType 屬性會被重賦值爲一個由 React.createContext() 建立的 Context 對象。這能讓你使用 this.context 來消費最近 Context 上的那個值。
    return <Button theme={this.context} />; } } 複製代碼

4.中間無需使用theme的組件,不須要層層傳遞props

// 中間的組件不再必指明往下傳遞 theme 了。
function Toolbar(props) {
  return (
    <div> <ThemedButton /> </div>
  );
}
複製代碼
  1. 3中的代碼也可以使用Consumer(消費者), 他是專門消費Provider(生產者) 產生的數據。
class ThemedButton extends React.Component {
  // 指定 contextType 讀取當前的 theme context。
  // React 會往上找到最近的 theme Provider,而後使用它的值。
  // 在這個例子中,當前的 theme 值爲 「dark」。
  static contextType = ThemeContext;
  render() {
    return (
    	<ThemeContext.Consumer>
   	         //Consumer容器,能夠拿到上文傳遞下來的 theme 屬性,並能夠展現對應的值
      		{(theme) =>
      			<Button theme={theme} />
            }
        </ThemeContext.Consumer>
    );
  }
}
複製代碼

注意事項

1.由於每次Context值變動時,Consumer都會接受到相應的變化通知,這裏可能會有一些陷阱,當 Provider 的父組件進行重渲染時,可能會在 Consumers 組件中觸發意外的渲染。例如

class App extends React.Component {
  render() {
  //當每一次 Provider 重渲染時(即每次render時{something: 'something'}都指向一個新對象,拓展1),如下的代碼會重渲染全部下面的 consumers 組件,由於 value 屬性老是被賦值爲新的對象
    return (
      <Provider value={{something: 'something'}}> <Toolbar /> </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>
    );
  }
}
複製代碼

2.組件的複用性下降

拓展

1.引用類型 2.mobx-react / react-router / react-redux 均適用context,有興趣可自行探究。

相關文章
相關標籤/搜索