[譯]React Context

Context 經過組件樹提供了一個傳遞數據的方法,從而避免了在每個層級手動的傳遞 props 屬性。算法

在一個典型的 React 應用中,數據是經過 props 屬性由上向下(由父及子)的進行傳遞的,但這對於某些類型的屬性而言是極其繁瑣的(例如:地區偏好,UI主題),這是應用程序中許多組件都所須要的。 Context 提供了一種在組件之間共享此類值的方式,而沒必要經過組件樹的每一個層級顯式地傳遞 props 。app

什麼時候使用 Context

Context 設計目的是爲共享那些被認爲對於一個組件樹而言是「全局」的數據,例如當前認證的用戶、主題或首選語言。例如,在下面的代碼中,咱們經過一個「theme」屬性手動調整一個按鈕組件的樣式:ide

function ThemedButton(props) {
  return <Button theme={props.theme} />;
}

// 中間組件
function Toolbar(props) {
  // Toolbar 組件必須添加一個額外的 theme 屬性
  // 而後傳遞它給 ThemedButton 組件
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}

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

使用 context, 我能夠避免經過中間元素傳遞 props:函數

// 建立一個 theme Context,  默認 theme 的值爲 light
const ThemeContext = React.createContext('light');

function ThemedButton(props) {
  // ThemedButton 組件從 context 接收 theme
  return (
    <ThemeContext.Consumer>
      {theme => <Button {...props} theme={theme} />}
    </ThemeContext.Consumer>
  );
}

// 中間組件
function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

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

不要僅僅爲了不在幾個層級下的組件傳遞 props 而使用 context,它是被用於在多個層級的多個組件須要訪問相同數據的情景測試

API

React.createContext

const {Provider, Consumer} = React.createContext(defaultValue);

建立一對 { Provider, Consumer }。當 React 渲染 context 組件 Consumer 時,它將從組件樹的上層中最接近的匹配的 Provider 讀取當前的 context 值。 若是上層的組件樹沒有一個匹配的 Provider,而此時你須要渲染一個 Consumer 組件,那麼你能夠用到 defaultValue 。這有助於在不封裝它們的狀況下對組件進行測試this

Provider

<Provider value={/* some value */}>

React 組件容許 Consumers 訂閱 context 的改變。 接收一個 value 屬性傳遞給 Provider 的後代 Consumers。一個 Provider 能夠聯繫到多個 Consumers。Providers 能夠被嵌套以覆蓋組件樹內更深層次的值。設計

Consumer

<Consumer>
  {value => /* render something based on the context value */}
</Consumer>

一個能夠訂閱 context 變化的 React 組件。 接收一個 函數做爲子節點. 函數接收當前 context 的值並返回一個 React 節點。傳遞給函數的 value 將等於組件樹中上層 context 的最近的 Provider 的 value 屬性。若是 context 沒有 Provider ,那麼 value 參數將等於被傳遞給 createContext() 的 defaultValuecode

每當Provider的值發生改變時, 做爲Provider後代的全部Consumers都會從新渲染。 從Provider到其後代的Consumers傳播不受shouldComponentUpdate方法的約束,所以即便祖先組件退出更新時,後代Consumer也會被更新。 經過使用與Object.is相同的算法比較新值和舊值來肯定變化。io

例子

動態 Context

主題的動態值,一個更加複雜的例子:function

theme-context.js

export const themes = {
  light: {
    foreground: '#ffffff',
    background: '#222222',
  },
  dark: {
    foreground: '#000000',
    background: '#eeeeee',
  },
};

export const ThemeContext = React.createContext(
  themes.dark // 默認值
);

themed-button.js

import {ThemeContext} from './theme-context';

function ThemedButton(props) {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <button
          {...props}
          style={{backgroundColor: theme.background}}
        />

      )}
    </ThemeContext.Consumer>
  );
}

export default ThemedButton;

app.js

import {ThemeContext, themes} from './theme-context';
import ThemedButton from './themed-button';

// 一個使用到ThemedButton組件的中間組件
function Toolbar(props) {
  return (
    <ThemedButton onClick={props.changeTheme}>
      Change Theme
    </ThemedButton>
  );
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      theme: themes.light,
    };

    this.toggleTheme = () => {
      this.setState(state => ({
        theme:
          state.theme === themes.dark
            ? themes.light
            : themes.dark,
      }));
    };
  }

  render() {
    // ThemedButton 位於 ThemeProvider 內
    // 在外部使用時使用來自 state 裏面的 theme
    // 默認 dark theme
    return (
      <Page>
        <ThemeContext.Provider value={this.state.theme}>
          <Toolbar changeTheme={this.toggleTheme} />
        </ThemeContext.Provider>
        <Section>
          <ThemedButton />
        </Section>
      </Page>
    );
  }
}

ReactDOM.render(<App />, document.root);
相關文章
相關標籤/搜索