React Context Api Actions

Context Api 的正确用法

react 官方文档示例里只是用 context api 做了跨组件的状态的分享。。。但是没有跨组件的动作分享。怎么解决呢?动作即状态嘛:

import * as React from "react";
import { Component } from "react";
import { render } from "react-dom";

const Counter = React.createContext<CounterProvider>(null);

export class CounterProvider extends React.Component {
  state = this.props.initialState || { count: 0 };
  inc = () => {
    this.setState({ count: this.state.count + 1 });
  };
  dec = () => {
    this.setState({ count: this.state.count - 1 });
  };
  inc_async = () => {
    setTimeout(() => {
      this.setState({ count: this.state.count + 1 });
    }, 1000);
  };
  render() {
    // 必须抽出新对象,不可以直接把 this 作为 value 传给 Provider,那样会被阻断渲染
    // CounterProvider render 不会引起 this.props.children render,所以没有性能问题 https://github.com/facebook/react/issues/4067#issuecomment-110792425
    // 另外虽然 Provider render 不会引起 children render,但是 children 的生命周期是跟随 Provider 的生命周期的
    return (
      <Counter.Provider value={{ ...this as any }}>
        {this.props.children}
      </Counter.Provider>
    );
  }
}

export const CounterHoc = name => BaseComponent => p => (
  <Counter.Consumer>
    {counter => <BaseComponent {...{ ...p, [name]: counter }} />}
  </Counter.Consumer>
);

export const CounterConsumer = Counter.Consumer;
// 上面这种用法,完美支持 render-props 和 hoc 的用法。。。
// 有了这些,如果不需要时间回溯,也不追求单 store 结构,基本就可以换掉 redux 了

const App = () => (
  <CounterProvider>
    <CounterConsumer>
      {counter => (
        <div>
          <div>{counter.state.count}</div>
          <button onClick={counter.inc}>inc</button>
          <button onClick={counter.dec}>dec</button>
          <button onClick={counter.inc_async}>inc_async</button>
        </div>
      )}
    </CounterConsumer>
    <CounterConsumer>
      {counter => (
        <div>
          <div>{counter.state.count}</div>
          <button onClick={counter.inc}>inc</button>
          <button onClick={counter.dec}>dec</button>
          <button onClick={counter.inc_async}>inc_async</button>
        </div>
      )}
    </CounterConsumer>
  </CounterProvider>
);

render(<App />, document.getElementById("root"));

[top]

comments powered byDisqus