手写Mini Redux实现

Favori,

Redux

图:Nguyen Nhut

实现

我们用2个文件来实现store和connect的逻辑,用2个文件来做测试用例 目录结构如下:

.
├── connect.js //connect实现
├── index.jsx  //测试用例
├── reducer.js //测试用例的reducer
└── store.js  //store实现

store实现 store.js

主要实现全局数据仓库,getStore,dispatch,subscribe方法

// 迷你版redux实现,全局数据仓库构造函数
function createStore(reducers) {
  var state = {};
  var listeners = [];
  var getState = () => state;
  var dispatch = (actionObj) => {
    let [model, action] = actionObj.type.split("/"); 
    state = reducers[model](state, action);
    listeners.forEach((l) => l());
  };
  var subscribe = (listener) => {
    listeners.push(listener);
    return () => {
      listeners = listeners.filter((l) => l !== listener);
    };
  };
  return {
    getState,
    dispatch,
    subscribe,
  };
}
export default createStore;

connect实现, connect.js

connect实现主要使用了高阶组件HOC

import React, { useState, useMemo, useReducer } from "react";
import createStore from "./store";
import reducers from "./reducer";
 
function baseReducer(state, action) {
  return {
    ...state,
    ...action.payload,
  };
}
 
//迷你简易版redux-react实现
const ConnectAdvanced = ({ WrappedComponent, actualChildProps }) => {
  const renderedWrappedComponent = useMemo(
    () => <WrappedComponent {...actualChildProps} />,
    [WrappedComponent, actualChildProps]
  );
  return <>{renderedWrappedComponent}</>;
};
 
const ConnectFunction = (props) => {
  const { store, mapStateToProps, dispatch, state, component } = props;
  // let [actualProps, setActualProps] = useState(state);
  let [actualProps,dispatchBase] = useReducer(baseReducer,state)
  store.subscribe(function () {
    const storeData = store.getState();
    let state = mapStateToProps(storeData);
    // setActualProps(state);
    dispatchBase({
      type:'save',
      payload:state
    })
  });
  return (
    <ConnectAdvanced
      WrappedComponent={component}
      actualChildProps={{ ...actualProps, dispatch }}
    />
  );
};
 
const connect = (mapStateToProps) => {
  var store = window.store
    ? window.store
    : (window.store = createStore(reducers));
  const storeData = store.getState();
  let state = mapStateToProps(storeData);
  let dispatch = store.dispatch;
  return (component) => {
    return () => {
      return (
        <ConnectFunction
          component={component}
          store={store}
          mapStateToProps={mapStateToProps}
          dispatch={dispatch}
          state={state}
        />
      );
    };
  };
};
 
export default connect;

reducer实现, reducer.js

全局reducers

//数据处理器,纯函数
var countReducer = (state = {}, action) => {
  if (!action) return state;
  const { count = 0 } = state;
  console.log(action);
  switch (action) {
    case "INCREMENT":
      return { ...state, count: count + 1 };
    case "DECREMENT":
      return { ...state, count: count - 1 };
    default:
      return { ...state, count };
  }
};
export default {countReducer}

测试用例 index.js

import React from "react";
import connect from "./connect";
 
const Page = function (props) {
  const { count = 0, dispatch = () => {} } = props;
  console.log("count", count);
  return (
    <>
      <div id="counter">count:{count}</div>
      <button
        id="addBtn"
        onClick={() => {
          dispatch({ type: "countReducer/INCREMENT" });
        }}
      >
        addBtn
      </button>
      <button
        id="minusBtn"
        onClick={() => {
          dispatch({ type: "countReducer/DECREMENT" });
        }}
      >
        minusBtn
      </button>
    </>
  );
};
 
const connectedPage = connect(({ count }) => ({ count }))(Page);
export default connectedPage;