Каков наилучший подход к написанию действий redux, которым нужны данные из других действий

#reactjs #redux #react-redux

#reactjs #redux #реагировать-redux

Вопрос:

Я провел некоторое исследование возможных способов сделать это, но я не могу найти тот, который использует ту же архитектуру, что и в приложении, над которым я работаю. Например, в документах React говорится, что у нас должен быть метод, который выполняет HTTP-запрос, а затем вызывает действия в разных точках (при запуске запроса, при получении ответа и т. Д.). Но у нас есть другой подход. Мы используем действие, которое выполняет HTTP-вызов, а затем отправляет результат. Если быть более точным, мой вариант использования таков:

 // action to get resource A
getResourceA () {
  return dispatch => {
    const result = await axios.get('someLink');
    dispatch({
      type: GET_RES_A,
      payload: result
    });
  }; 
}

// another action which needs data from resource A
getSomethingElseByIdFromA (aId) {
  return async dispatch => {
    const result = await axiosClient.get(`someLink/${aId}`);
    dispatch({
      type: GET_SOMETHING_BY_ID_FROM_A,
      payload: result
    });
  }; 
}
  

Как указано, для второго действия требуются данные из первого.

Теперь я знаю два способа сделать это:

  1. возвращает результат первого действия
 getResourceA () {
  return async dispatch => {
    const result = await axios.get('someLink');
    dispatch({
      type: GET_RES_A,
      payload: result
    });
    return resu<
  }; 
}

// and then, when using it, inside a container
async foo () {
  const {
    // these two props are mapped to the getResourceA and
    // getSomethingElseByIdFromA actions
    dispatchGetResourceA,
    dispatchGetSomethingElseByIdFromA
  } = this.props;

  const aRes = await dispatchGetResourceA();
  // now aRes contains the resource from the server, but it has not
  // passed through the redux store yet. It's raw data
  dispatchGetSomethingElseByIdFromA(aRes.id);
}
  

Однако проект, над которым я работаю прямо сейчас, хочет, чтобы данные сначала проходили через хранилище — на случай, если его необходимо изменить, — и только после этого его можно использовать. Это привело меня ко 2-му способу действий:

  1. создайте «агрегированный» сервис и используйте getState метод для доступа к состоянию после завершения действия.
 aggregateAction () {
  return await (dispatch, getState) => {
    await dispatch(getResourceA());
    const { aRes } = getState();
    dispatch(getSomethingElseByIdFromA(aRes.id));
  };
}
  

И после этого просто вызовите это действие в контейнере.

Мне интересно, подходит ли второй способ. Я чувствую, что нехорошо иметь вещи в хранилище redux только ради доступа к ним во время действий. Если это так, каков был бы лучший подход к этой проблеме?

Ответ №1:

Я думаю, что наличие / использование Epic from redux-observable было бы наилучшим образом подходящим для вашего варианта использования. Это позволило бы сначала выполнять действия во всех ваших редукторах (в отличие от упомянутого выше подхода), прежде чем обрабатывать их в ТОЙ ЖЕ логике. Кроме того, использование потока действий позволит вам манипулировать данными во всем их потоке, и вам не придется хранить ненужные вещи. Реактивное программирование и сам наблюдаемый шаблон имеют некоторые большие преимущества, когда дело доходит до асинхронных операций, лучший вариант, чем redux-thunk, sagas и т. Д. imo.

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

1. Я взглянул на библиотеку redux-observable. Это действительно возможно и может решить проблему. Однако мне бы понравился подход, который не требовал бы использования другой библиотеки. Я также узнал о новых пакетных действиях, представленных Redux в их последнем обновлении. Возможно, они могли бы помочь.

Ответ №2:

Я бы взглянул на использование пользовательского промежуточного программного обеспечения (https://redux.js.org/advanced/middleware ). Использование промежуточного программного обеспечения может упростить достижение такого рода целей.

Что-то вроде :

  import {GET_RESOURCE_A, GET_RESOURCE_B, GET_RESOURCE_A_SUCCESS, GET_RESOURCE_A_ERROR  } from '../actions/actionTypes'

    const actionTypes = [GET_RESOURCE_A, GET_RESOURCE_B, GET_RESOURCE_A_SUCCESS, GET_RESOURCE_A_ERROR ]

    export default ({dispatch, getState}) => {

        return next => action => {

            if (!action.type || !actionTypes.includes(action.type)) {

                return next(action)

            }

            if(action.type === GET_RESOURCE_A){
              try{
                // here you can getState() to look at current state object 
                // dispatch multiple actions like GET_RESOURCE_B and/ or 
                // GET_RESOURCE_A_SUCCESS
                // make other api calls etc....
               // you don't have to keep stuff in global state you don't 
               //want to you could have a varaiable here to do it
              }
             catch (e){

              }  dispatch({type:GET_RESOURCE_A_ERROR , payload: 'error'})
            }

        }

    }
  

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

1. Извините, я не понимаю ваш пример, и я не думаю, что промежуточное программное обеспечение — это то, что я ищу. Промежуточное программное обеспечение будет вызываться каждый раз, когда я отправляю действие. Я просто хочу, чтобы действие использовало данные, полученные в другом действии. Промежуточное программное обеспечение, похоже, перестраивает вещи.

2. Но, конечно, вы можете перехватить действие в промежуточном программном обеспечении, использовать его по своему усмотрению и получить текущее состояние, если оно вам нужно в этом сравнении, и отправить второе действие, но оно обойдет промежуточное программное обеспечение, если его нет в вашем массиве ActionTypes ? Или я неправильно понимаю (очень возможно)