Warning: in_array() expects parameter 2 to be array, null given in /data/wwwroot/blog.catui.co/usr/themes/Plain/header.php on line 93

动手开始编写一个简易的状态管理

:).png

在此之前先创建 React 基础模版,这里使用的是 CRA!

npx create-react-app app
cd app
mkdir R # 将在这里存放
npm start

最终 API 参考 Vuex;

const state = {
  counter: 0
};

const actions = {
  increment(state, data) {
    state.counter += data;
  },
  decrement(state, data) {
    state.counter -= data;
  }
};

connect({
  state: ['counter'],
  actions: ['increment', 'decrement']
})(Component);

创建 Store

首先创建 Store 存放的模型;

export default class Store {
  constructor(state = {}, actions = {}) {
    this.state = this.state;
    this.actions = this.rewriteActions(state, actions);
  }

  // 最终 action 是以 xxAction(data) 传入组建使用,所以这里改造一下。
  rewriteActions(state = {}, actions = {}) {
    Object.keys(actions).forEach(key => {
      const fn = actions[key];
      actions[key] = function(data) {
        fn(state, data);
        console.log(this.state);
      };
    });
  }
}

创建 Provider

创建 Store Provider 以提供给 React 。

import React from 'react';

//创建 Context
const Context = React.createContext(null);

export class Provider extends React.Component {
  render() {
    console.log(this.store);
    return (
      <Context.Provider value={this.props.store;}>{this.props.children}</Context.Provider>
    );
  }
}

创建链接组建 Connect

其实就是一个 HOC;

function getProps(chunk = [], source = {}) {
  const res = {};
  chunk.forEach(k => {
    res[k] = source[k];
  });
  return res;
}

export const connect = (state = [], actions = []) => {
  return class extends React.Component {
    static contextType = Context;
    constructor(props) {
      super(props);
      this.state = {
        store: {}
      };
    }
    componentDidMount() {
      this.setState({
        store: {
          ...getProps(state, this.context.state),
          ...getProps(actions, this.context.actions)
        }
      });
    }
  };
};

试试链接起来!

import Store from './R/store';
import { connect, Provider } from './R/connect';

const state = {
  counter: 0
};

const actions = {
  increment(state, data) {
    state.counter += data;
  },
  decrement(state, data) {
    state.counter -= data;
  }
};

const store = new Store({ state, actions });

@connect({
  state: ['counter'],
  actions: ['increment', 'decrement']
})
class App extends React.Component {
  render() {
    return (
      <div className="App">
        <p>{this.props.count}</p>
        <button onClick={() => this.props.increment(1)}>+</button>
        <button onClick={() => this.props.decrement(1)}>-</button>
      </div>
    );
  }
}

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,

  document.getElementById('root')
);

点击 + 号,可以看到我们的 Store 值确实是改变了但是为什么页面没有更新呢?

:(.jpg

订阅更新

当每次执行 action 时进行 setState 。

//store
actions[key] = function(data) {
  fn(state, data);
  console.log(this.state);
  this.subs.forEach(sub => sub()); // add
}.bind(this);
// ...

  subscribe(sub) {
    this.subs.push(sub);
  }
  unSubscribe(sub) {
    this.subs = this.subs.filter(f => f !== sub);
  }

// connect
    componentDidMount() {
      this.upSubs();
      this.context.subscribe(() => this.upSubs());
    }

    componentWillMount(){
        this.context.unSubscribe(()=>this.upSubs)
    }

    upSubs() {
      this.setState({
        store: {
          ...getProps(state, this.context.state),
          ...getProps(actions, this.context.actions)
        }
    });

大功告成!
:).png

ref:

preView