blog icon indicating copy to clipboard operation
blog copied to clipboard

全新的 React Context API

Open Hancoson opened this issue 6 years ago • 3 comments

在一个经典的 React 应用中,组件之间通信是常用到的技术方案。在父子组件之间通常通过 props 来传递参数,而非父子组件就比较麻烦了,要么就一级一级通过 props 传递,要么就使用 Redux or Mobx 这类状态管理的状态管理库,但是这样无疑增加了应用的复杂度。在 FEers 的期盼中,React 团队终于从 16.3.0 版本开始新增了一个新的 API Context,福音啊。好了,今天我就来一起学习一下这个新的 Context

什么时候使用 Contsxt

Context 目的是为了共享可以被认为是 React 组件“全局”树的数据。例如当前应用的主题、首选语言等等。接下来看看通过 propsContext 两种方式实现按钮组件样式参数传递方式的对比:

  • props
class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}
Toolbar(props) {
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}
ThemedButton(props) {
  return <Button theme={props.theme} />;
}
  • Context
// 创建 context 实例
const ThemeContext = React.createContext('light');

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

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

API

React.createContext

创建一个 ContextReact.createContext 提供了 {Provider,Comsumer} 两个方法,上面的代码也可以这个来写:

const {Provider,Comsumer} = React.createContext('light');

class App extends React.Component {
  render() {
    return (
      <Provider value="dark">
        {/* ... */}
      </Provider>
    );
  }
}
{/* ... */}
ThemedButton(props) {
  return (
    <Consumer>
      {/* ... */}
    </Consumer>
  );
}

Provider

这里的 Provider 类似 react-redux 中的 Provider 组件,用来注入全局的 data (允许 Consumer 订阅 Context 的变化)。一个 Provider 可以连接到多个 Consumer

Consumer

Consumer 组件,表示要消费 Provider 传递的数据(订阅 Context 的响应组件)。当 Provider 发生变化的时候,所有的 Consumer 都会被 re-rendered

结束语

Context 的引入,一定程度上可以减少不少项目对 redux 全家桶的依赖,从而降低了项目的复杂程度,何乐而不为呢~~

Hancoson avatar Apr 08 '18 03:04 Hancoson

React 16+ 新的生命周期

v2-50df8407730530985712a313ae020041_r

Hancoson avatar May 25 '18 01:05 Hancoson

consumer 好像并没能订阅 context 的变化,只能获取初始值,是不是我用的不对? 我是这样用的: 在父组件:

import Child1 from './child1'

const defaultContext = {
  a: 0,
  setA: function (a) {
    this.a = a
  }
}

export const Context = createContext()

export default class Father extends React.Component {
  render() {
    return <Context.Provider value={defaultContext}>
        <Child1/>
    </Context.Provider>
  }
}

在子组件:

class Child1 extends React.Component {
  render() {
    const {context} = this.props
    return<div style={{backgroundColor: '#897', padding: '29px'}}>
      子组件1
      <div>a:{context.a}</div>
      <input placeholder='输入' onChange={(e) => context.setA(e.target.value)}></input>
    </div>
  }
}

export default () => <Context.Consumer>{
  (context) => {
   return <Child1 context={context}/>
  }
}</Context.Consumer>

我在子组件中调用 context.setA() 修改了 context.a,但是子组件并没有更新

qumuchegi avatar Oct 22 '20 09:10 qumuchegi

知道为啥子组件没有更新了,因为组件更新需要 state / props 变化,但是 context value 这两者都不是

qumuchegi avatar Nov 03 '20 05:11 qumuchegi