【瞎写代码】系列之redux表面理解

689 阅读3分钟

最近在翻boss,感觉今年前端的风向标的确是react,火爆程度完全超过了其他框架,所以为了跟上潮流,不得不开始使用react了,既然用了react就免不了要研究一下redux。关于redux的使用场景,就看自己得业务需要了,就不多说了。这里就简单描述一下redux的使用原理和一些核心的结构,主要针对一些还没有理解redux工作流的童鞋。 先看一下redux的流程图

这是一个非常非常简单的工作流程图,通过交互触发action,这时候会创建一个action对象,然后将这个对象丢给reducers,reducers会根据这个action的行为,对其携带的数据进行处理,然后merge到state,这里就需要一个方法来完成这个过程,这个就是dispatch,state发生变化后,触发监听(这个应该算是个发布订阅)。这应该算是一个比较好理解的工具了,过程并不复杂,这个过程中reducer的逻辑是注入进来的,所以这个方法传进来就好。。好,这样我们就知道redux的核心结构是什么样的了:

class MyRedux {
  constructor(reducer, defaultState) {
    this.reducer = reducer;
    this.subscribeIndex = 0;
    this.state = defaultState
      ? { ...defaultState }
      : this.reducer(undefined, {
          type: "qweqwe"
        });
    this.subscribeMap = {};
  }
  subscribe = func => {
    let indexKey = this.subscribeIndex++;
    this.subscribeMap[indexKey] = func;
    return () => {
      delete this.subscribeMap[indexKey];
    };
  };
  getState = () => {
    return this.state;
  };
  dispatch = action => {
    this.state = Object.assign(this.state, this.reducer(this.state, action));
      Object.keys(this.subscribeMap).forEach(key => {
      let subscribeFunc = this.subscribeMap[key];
          subscribeFunc && subscribeFunc();
    });
  };
}

大概解释一下,constructor里面就是接受reducers和初始化state,这里的'qweqwe'是随便写的,源码中用的是'@@redux/INIT',subscribe就是注册监听,dispatch就是接受action然后通过reducers改变state,接着就触发注册的监听。 应该很好理解吧,再看一下redux其他的一些方法。

export function CreateStore(reducer) {
  return new MyRedux(reducer);
}
export function combineReducers(reducerMap) {
  return function(state = {}, action) {
    const nextState = {};
    Object.keys(reducerMap).forEach(key => {
      const reducer = reducerMap[key];
      nextState[key] = reducer(state[key], action);
    });
    return nextState;
  };
}

这两个方法就比较简单了,创建store和合并reducers。 接下来就是provider,这个其实只要理解了context就好办了,来我们直接看代码:

const MyReactContext = React.createContext();

export class MyProvider extends Component {
  static propTypes = {
    store: PropTypes.instanceOf(MyRedux),
    children: PropTypes.node
  };
  render() {
    const MyReactProvider = MyReactContext.Provider;
    return (
      <MyReactProvider value={this.props.store}>
        {this.props.children}
      </MyReactProvider>
    );
  }
}

这是一个react节点,直接创建一个context,然后把子节点扔在provider里面就行了,这样就能在这个里面通过consumer获取到store了。最后就是connect了,这个对一些初学者来说还是有些绕的,使用的时候需要传入两次参数,一次是需要并入props的一些参数,一个是当前的组件,这块是柯里化一个很好的应用,个人感觉柯里化的好处就是能把参数分类,切换频次低的可以复用,不过用的还是比较少,理解太到位。接着说connect,这个是高阶组件的用法,我们在外层函数传入并入的参数,然后返回一个函数,这个函数接受一个react组件,返回一个新的包装过后的组件。来看一下代码:

export function MyConnect(mapStateToProps, mapDispatchToProps) {
  function connectHOC(RawComponent) {
    return class NewComponent extends Component {
      unsubscribe = null;

      constructor(props) {
        super(props);
        this.state = {
        };
      }

      setSubscribe(store) {
        if (!this.unsubscribe) {
          this.unsubscribe = store.subscribe(() => {
            this.setState(prevState => {
                return {
                };
            });
          });
        }
      }

      componentWillUnmount() {
        this.unsubscribe && this.unsubscribe();
      }

      getState = store => {
        if (!mapStateToProps) {
          return store.getState();
        } else {
          return mapStateToProps(store.getState(), this.props);
        }
      };

      getDispatch = store => {
        if (!mapDispatchToProps) {
          return {
            dispatch: store.dispatch
          };
        } else {
          return mapDispatchToProps(store.dispatch, this.props);
        }
      };

      render() {
        const MyReactConsumer = MyReactContext.Consumer;
        return (
          <MyReactConsumer>
            {store => {
                this.setSubscribe(store);
                let mixedProps = {
                  ...this.getState(store),
                  ...this.getDispatch(store),
                  ...this.props
               }
              return <RawComponent store={store} {...mixedProps} />;
            }}
          </MyReactConsumer>
        );
      }
    };
  }

  return connectHOC;
}

这就是connect方法,通过高阶组件返回一个被包装起来的组件,这样就能消费store了,还有在render 的时候,我们要把监听事件注册进来,这个方法目前只是用来重新渲染,这个地方用的是函数式的方法,因为setState是异步的,不可信(这个地方只是为了重新渲染,不要在意没有东西。。。)。不过个人认为这个地方可以做更多的事情,不过老师告诉我没有实际场景就不要过度设计,属于伪设计(受教了。。。),最后要记得删除监听。
以上就是简易的redux代码,应该还比较好理解的把。感觉能看的下去的话,麻烦点个赞哈。。


最后,,,求一份内推。。。