[译]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);
相关文章
相关标签/搜索