Извлекает текущее состояние из пользовательского интерфейса вне компонента React

#reactjs #redux #react-hooks

#reactjs ( реакция ) #сокращение #реагирующие крючки

Вопрос:

Я использую useReducer хук с контекстом для создания хранилища состояний 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>
  );
};
  

Обратите внимание, что я перехожу state к applyMiddleware :

 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);
  };
  

Это работает, но в конечном итоге я хочу иметь возможность работать с асинхронными действиями, поэтому в идеале у меня было бы что-то вроде redux-thunk :

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

Учитывая, что промежуточное программное обеспечение thunk будет реагировать на асинхронные действия, в идеале мы могли бы передать функцию для извлечения текущего состояния при необходимости — getState — вместо того, чтобы быть вынужденными использовать состояние, которое существовало при применении промежуточного программного обеспечения, которое может устареть.

Обычно я бы передал что-то вроде этого:

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

Но если я передам это промежуточному программному обеспечению для вызова, я получу сообщение об ошибке, указывающее, что я могу вызывать перехваты только из функций 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;
};
  

Комментарии:

1. Что-то вроде github.com/jamesseanwright/roll-your-own-redux/blob/master/src /… ?

2. Как makeActionDispatcher выглядит ваш

3. @Federkun Глядя на реализацию, кажется, что для любых асинхронных операций также невозможно получить текущее состояние, только состояние, когда отправка была увеличена.

4. добавил @fard — но я не знаю, насколько это уместно для вопроса.

5. Вы пытаетесь использовать перехваты вне контекста react. Хуки разработаны таким образом, что вы можете использовать их только в функциональном компоненте react. В вашем случае, поскольку вам необходимо обрабатывать изменения хранилища вне компонента, вы можете попробовать перенести свое состояние во внешнюю функцию и создать пользовательский хук reactjs.org/docs/hooks-custom.html

Ответ №1:

Используйте useEnhancedReducer хук, представленный здесь.

Тогда у вас будет что-то вроде.

 const [state, dispatch, getState] = useEnahancedReducer(reducer, initState)
  

Поскольку dispatch , getState никогда не изменится, вы можете передать его какому-либо перехватчику, не добавляя их в список зависимостей, или сохранить их где-нибудь еще, чтобы вызывать их извне.

Существует также версия useEnhancedReducer , которая поддерживает добавление промежуточного программного обеспечения, в том же посте.