从 React 组件外的 useReducer 检索当前状态

Mis*_*pic 5 reactjs redux react-hooks

我正在利用useReducer带有 Context的钩子来创建支持中间件的 Redux-ish 状态存储。

const Provider = (props: any) => {
  const [state, dispatch] = React.useReducer(reducer, {
    title: 'Default title',
    count: 0,
  });

  const actionDispatcher = makeActionDispatcher(
    dispatch,
    applyMiddleware(state, thunkMiddleware, callApiMiddleware, logger),
  );

  return (
    <Context.Provider value={{ ...state, ...actionDispatcher }}>
      {props.children}
    </Context.Provider>
  );
};
Run Code Online (Sandbox Code Playgroud)

请注意,我正在传递stateapplyMiddleware

const applyMiddleware = (state: {}, ...middlewares: Function[]) =>
  function dispatcher(dispatch: Function) {
    const middlewareAPI = {
      state,
      dispatch: (...args) => dispatch(...args),
    };
    const chain = middlewares.map((middleware) => {
      return middleware(middlewareAPI);
    });
    return compose(...chain)(dispatch);
  };
Run Code Online (Sandbox Code Playgroud)

这有效,但最终我希望能够使用异步操作,所以理想情况下我会有类似的东西redux-thunk

function thunkMiddleware(store: Store) {
  return (next: Function) => (action: any) => {
    typeof action === 'function' ? action(next, store.getState) : next(action);
  };
}
Run Code Online (Sandbox Code Playgroud)

鉴于 thunk 中间件将作用于异步操作,理想情况下,我们将能够在需要时传递一个函数来检索当前状态getState——而不是被迫使用应用中间件时存在的状态,这可能超出日期。

通常我会传递这样的东西:

const getState = () => React.useReducer(reducer, {
    title: 'Default title',
    count: 0,
  })[0];
Run Code Online (Sandbox Code Playgroud)

但是如果我将它传递给要调用的中间件,我会收到一个错误,表明我只能从 React 函数调用钩子

我设计的东西错了吗?我是不是没有正确地用钩子缠住我的头?

更新:添加请求的makeActionDispatcher实现

export const makeActionDispatcher = (
  dispatch: React.Dispatch<any> | undefined,
  enhancer?: Function,
): ActionDispatcher => {
  const actionDispatcher: { [key: string]: (...args: any) => void } = {};

  Object.keys(actionCreators).forEach((key) => {
    const creator = actionCreators[key];
    actionDispatcher[key] = (...args: any) => {
      if (!dispatch) {
        throw new Error('ActionDispatcher has not been initialized!');
      }

      const action = creator(...args);

      if (enhancer) {
        const enhancedDispatch = enhancer(dispatch);
        enhancedDispatch(action);
      } else {
        dispatch(action);
      }
    };
  });

  return actionDispatcher as ActionDispatcher;
};
Run Code Online (Sandbox Code Playgroud)

tra*_*ang 2

使用这里useEnhancedReducer介绍的钩子。

然后你就会有类似的东西。

const [state, dispatch, getState] = useEnahancedReducer(reducer, initState)
Run Code Online (Sandbox Code Playgroud)

因为dispatch,getState永远不会改变,所以您可以将其传递给某个钩子,而无需将它们添加到依赖列表中或将它们存储在其他地方以便从外部调用它们。

在同一篇文章中还有useEnhancedReducer支持添加中间件的版本。