# React Context API

context 就是用来设计分享全局数据的。但是，官方并不推荐使（滥）用 `context` ，因为这是一个实验性的 API，在未来仍会变动，除非你打算实现一个类似于 React-Router、Redux 之类的全局组件。

## 旧版本的 context API 的 shouldComponentUpdate 问题

如果使用了上下文组件的父组件中`shouldComponentUpdate()`返回了`false`，则该子组件并不会更新。

```jsx
import React, { Component } from 'react';
import PropTypes from 'prop-types';

class CComponent extends Component {

    static contextTypes = {
        color: PropTypes.string
    }

    render() {
        return (
            <span>{this.context.color}</span>
        );
    }
}

class MComponent extends Component {
    shouldComponentUpdate() {
        return false;
    }
    render() {
        return (
            <CComponent />
        );
    }
}

class Root extends Component {
    static childContextTypes = {
        color: PropTypes.string
    }
    getChildContext() {
        return {
            color: 'red',
        }
    }

    render() {
        return (
            <MComponent />
        );
    }
}
```

## 旧版本 Context API Key 冲突

旧版本的 Context API 中，如果子组件的 父组件 A 和父组件 B 的 Context key 相同，会产生冲突

```jsx
import React, { Component } from 'react';
import PropTypes from 'prop-types';

class CComponent extends Component {

    static contextTypes = {
        color: PropTypes.string
    }

    render() {
        return (
            <span>{this.context.color}</span>
        );
    }
}

class MComponent extends Component {
    static childContextTypes = {
        color: PropTypes.string
    }

    getChildContext() {
        return {
            color: 'blue',
        }
    }

    render() {
        return (
            <CComponent onClick={this.props.onClick} />
        );
    }
}

class Root extends Component {
    static childContextTypes = {
        color: PropTypes.string
    }

    getChildContext() {
        return {
            color: 'red',
        }
    }

    render() {
        return (
            <MComponent />
        );
    }
}
```

## 新版本 Context API 的使用

React v16.3.0 推出了新的 Context API，其中包括：

* React.createContext(defaultValue) // 创建一个 `Context` 对象
* Provider // 容器组建，类似于 Redux 的 `Provider`
* Consumer // 消费组件，消费 Provider 提供的数据

```jsx
import React, { Component, createContext } from 'react';

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

export const ThemeContext = createContext({
  theme: themes.dark,
  toggleTheme: () => {},
});

function ThemeTogglerButton(props) {
  // The Theme Toggler Button receives not only the theme
  // but also a toggleTheme function from the context
  return (
    <ThemeContext.Consumer>
      {({theme, toggleTheme}) => (
        <button
          onClick={toggleTheme}
          style={{backgroundColor: theme.background}}>
          Toggle Theme
        </button>
      )}
    </ThemeContext.Consumer>
  );
}

// An intermediate component that uses the ThemedButton
class Toolbar extends Component {
  shouldComponentUpdate() {
    return false
  }
  render() {
    return (
      <ThemeTogglerButton onClick={this.props.changeTheme}>
        Change Theme
      </ThemeTogglerButton>
    );
  }
}

class App extends Component {
  constructor(props) {
    super(props);

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

  render() {
    // The entire state is passed to the provider
    return (
      <ThemeContext.Provider value={this.state}>
        <Toolbar changeTheme={this.toggleTheme} />
      </ThemeContext.Provider>
    );
  }
}
```

新版本的 Context API 解决了之前提到的旧版本 Context API 存在的问题。

> *如果组件的层级只有几层的话，不建议采用 Context*

## 使用新版本 Context API 取代 Redux？

就目前来说，新版的 Context API 仍旧无法解决 Redux 能够解决的下问题：

* **逻辑/数据/视图分离的代码结构**
* **在不同项目之间通用的存储和事件机制**，从而允许`redux-devtools`这种通用的开发工具、以及类似`redux-observable`这种强大中间件的存在

## 新版 Context API 的其它问题

新版 Context API 仍存在其它值得思考的方向，由于不限制 Context 的数量，因此引发了以下几个问题。

* 多个 Context 如何管理
* 子组件消费多个 Context 导致的多层 Comsumer 嵌套问题
* 子组件更新 Context 值，需要向 `Provider` 传递 handler，这并不符合单一职责原则
* 子组件触发多个 Context 状态更新，导致的多次更新问题


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xyy94813.gitbook.io/x-note/react/react-context-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
