React 基礎篇(七)—— Context

Context

Context 提供了一個無需爲每層組件手動添加 props,就能在組件樹間進行數據傳遞的方法。

一般情況下,數據是通過 props 屬性自上而下進行傳遞的,但這種做法對於某些類型的屬性是極其繁瑣的,比如主題,這些屬性是應用程序中許多組件都需要的。Context 提供了一種在組件之間共享此類值的方式,而不必顯示地通過組件樹逐層傳遞 props

Context 設計目的是爲了共享那些對於一個組件樹而言是 “全局” 的數據,比如前面提到的主題。在下面代碼中通過一個 theme 屬性手動調整一個按鈕組件的樣式:

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} />;
  }
}

使用 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} />;
  }
}

Context 主要應用場景在於很多不同層級的組件需要訪問同樣的一些數據。如果只是想避免層層傳遞一些屬性,組件組合會比 context 更好。

相關的API

React.createContext

const MyContext = React.createContext(defaultValue);

創建一個 Context 對象,當 React 渲染一個訂閱了這個 Context 對象的組件,這個組件會從組件樹中離自身最近的那個匹配的 Provider 中讀取到當前的 context 值。只有當組件所處的樹中沒有匹配到 Provider 時,其 defaultValue 參數纔會生效。

Context.Provider

<MyContext.Provider value={/* 某個值 */}>

每個 Context 對象都會返回一個 Provider React 組件,它允許消費組件訂閱 context 的變化。Provider 接收一個 value 屬性,傳遞給消費組件,一個 Provider 可以和多個消費組件有對應關係。多個 Provider 也可以嵌套使用,裏面的會覆蓋外層的數據。

Providervalue 值發生變化時,它內部的所有消費組件都會重新渲染。Provider 及其內部的 consumer 組件都不受制於 shouldComponentUpdate ,因此當 consumer 組件在其祖先退出更新的情況下也能更新。

需要注意的是,新舊值的變化通過 Object.is 一樣的算法來完成的。

Class.contextType

class MyClass extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* 在組件掛載完成後,使用 MyContext 組件的值來執行一些有副作用的操作 */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* 基於 MyContext 組件的值進行渲染 */
  }
}
MyClass.contextType = MyContext;

掛載在 class 上的 contextType 屬性會被重賦值爲一個由 React.createContext() 創建的 Context 對象。這能讓我們使用 this.context 來消費最近 Context 上的那個值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章