先来看一个基本的 redux 使用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const initState = {
  count: 0
};

function reducer(state, action) {
  switch (action.type) {
    case "INCREMENT":
      return { ...state, count: state.count + 1 };
    case "DECREMENT":
      return { ...state, count: state.count - 1 };
    case "RESET":
      return { ...state, count: 0 };
    default:
      return state;
  }
}

const store = createStore(reducer, initState);

// 订阅变化,结合 react 的话页面更新的逻辑在这里处理
store.subscribe(() => {
  console.log(store.getState(), "subscribe");
});

// 派发 action
store.dispatch({ type: 'INCREMENT' });
store.dispatch({ type: 'DECREMENT' });
store.dispatch({ type: 'RESET' });

基于上述可以实现一个简单的 redux:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function createStore(reducer, initState) {
  let state = initState;
  let listeners = [];

  function subscribe(callback) {
    listeners.push(callback);
  }

  function dispatch(action) {
    state = reducer(state, action);

    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i];
      listener();
    }
  }

  function getState() {
    return state;
  }

  return {
    getState,
    subscribe,
    dispatch
  };
}

再下来实现结合 react 的 connect 方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
function connect(mapStateToProps, mapDispatchToProps) {
  return function (WrappedComponent) {
    return function NewComponent(...selfProps) {
      const context = useContext(ReduxContext); // 需要借助 context,从 context 注入
      const { store } = context;
      const [stateProps, setStateProps] = useState({});

      function update() {
        const newState = mapStateToProps(store.getState());
        const newDispatch = mapDispatchToProps(store.dispatch);
        setStateProps({ ...newState, ...newDispatch });
      }

      useEffect(() => {
        const unsubscribe = store.subscribe(() => update());
        update();
      }, []);

      return <WrappedComponent {...selfProps} {...stateProps} />;
    };
  };
}

看到这里需要借助 context:

1
2
3
4
5
6
7
8
9
const store = createStore(reducer, initState);
const ReduxContext = React.createContext();

function Provider(props) {
  const { store, children } = props;
  return (
    <ReduxContext.Provider value={{ store }}>{children}</ReduxContext.Provider>
  );
}

使用时就可以像下面这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const App = (props) => {
  return (
    <div>
      <button onClick={props.increase}>点击加1</button>
      <button onClick={props.decrease}>点击减1</button>
      <button onClick={props.reset}>重置</button>
      <h1>{props.count}</h1>
    </div>
  );
};

const mapStateToProps = (state) => {
  return {
    count: state.count
  };
};

const mapDispatchToProps = (dispatch) => ({
  increase: () => dispatch({ type: "INCREMENT" }),
  decrease: () => dispatch({ type: "DECREMENT" }),
  reset: () => dispatch({ type: "RESET" })
});

const NewApp = connect(mapStateToProps, mapDispatchToProps)(App);

ReactDOM.render(
  <Provider store={store}>
    <NewApp />
  </Provider>,
  document.getElementById("app")
);