Каков наилучший способ справиться с ошибкой выборки в react redux?


У меня есть один редуктор для клиентов, один другой для AppToolbar и некоторые другие...

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

но клиенты и редукторы AppToolbar не разделяют одну и ту же часть состояния, и я не могу создать новое действие в редукторе.

Так как я предполагаю показать глобальную ошибку? Спасибо

обновление 1:

Я забыл упомянуть, что я использую Эстэ devstack

обновление 2: Я отметил ответ Эрика как правильный, но я должен сказать, что решение, которое я использую в este, больше похоже на комбинацию ответа Эрика и Дэна... Вы просто должны найти то, что подходит вам лучше всего в вашем коде...

6 67

6 ответов:

если вы хотите иметь понятие "глобальные ошибки", вы можете создать errors редуктор, который может слушать для addError, removeError, etc... действия. Затем вы можете подключиться к дереву состояний Redux по адресу state.errors и отображать их там, где это необходимо.

есть несколько способов, которыми вы могли бы подойти к этому, но общая идея заключается в том, что глобальные ошибки/сообщения заслуживают того, чтобы их собственный редуктор жил полностью отдельно от <Clients />/<AppToolbar />. Конечно, если любой из них компонентам необходим доступ к errors вы могли бы пройти errors им в качестве опоры, где требуется.

Обновление: Пример Кода

вот один пример того, как это может выглядеть, если вы должны были передать "глобальные ошибки" errors на ваш верхний уровень <App /> и условно визуализировать его (если есть ошибки). Используя реагировать-возвращение подключить <App /> компонент к некоторым данным.

// App.js
// Display "global errors" when they are present
function App({errors}) {
  return (
    <div>
      {errors && 
        <UserErrors errors={errors} />
      }
      <AppToolbar />
      <Clients />
    </div>
  )
}

// Hook up App to be a container (react-redux)
export default connect(
  state => ({
    errors: state.errors,
  })
)(App);

и как что касается создателя действия, он отправит (redux-thunk) неудача успеха в соответствии с ответом

export function fetchSomeResources() {
  return dispatch => {
    // Async action is starting...
    dispatch({type: FETCH_RESOURCES});

    someHttpClient.get('/resources')

      // Async action succeeded...
      .then(res => {
        dispatch({type: FETCH_RESOURCES_SUCCESS, data: res.body});
      })

      // Async action failed...
      .catch(err => {
        // Dispatch specific "some resources failed" if needed...
        dispatch({type: FETCH_RESOURCES_FAIL});

        // Dispatch the generic "global errors" action
        // This is what makes its way into state.errors
        dispatch({type: ADD_ERROR, error: err});
      });
  };
}

в то время как ваш редуктор может просто управлять массивом ошибок, добавляя/удаляя записи соответствующим образом.

function errors(state = [], action) {
  switch (action.type) {

    case ADD_ERROR:
      return state.concat([action.error]);

    case REMOVE_ERROR:
      return state.filter((error, i) => i !== action.index);

    default:
      return state;
  }
}

Эрика!--5--> правильно, но я хотел бы добавить, что вам не нужно запускать отдельные действия для добавления ошибок. Альтернативный подход - иметь редуктор, который обрабатывает любое действие с error поле. Это вопрос личного выбора и условностей.

например,возвращение real-world пример который имеет обработку ошибок:

// Updates error message to notify about the failed fetches.
function errorMessage(state = null, action) {
  const { type, error } = action

  if (type === ActionTypes.RESET_ERROR_MESSAGE) {
    return null
  } else if (error) {
    return action.error
  }

  return state
}

подход, который я сейчас использую для нескольких конкретных ошибок (проверка ввода пользователем), заключается в том, чтобы мои субредукторы выбрасывали исключение, ловили его в моем корневом редукторе и прикрепляли его к объекту действия. Затем у меня есть redux-saga, который проверяет объекты действия на наличие ошибки и обновляет дерево состояний с данными об ошибках в этом случае.

Так:

function rootReducer(state, action) {
  try {
    // sub-reducer(s)
    state = someOtherReducer(state,action);
  } catch (e) {
    action.error = e;
  }
  return state;
}

// and then in the saga, registered to take every action:
function *errorHandler(action) {
  if (action.error) {
     yield put(errorActionCreator(error));
  }
}

а затем добавить ошибку в дерево состояний, как описывает Эрик.

Я использую его довольно редко, но это удерживает меня от необходимости дублировать логику, которая законно принадлежит редуктору (чтобы он мог защитить себя от недопустимого состояния).

напишите пользовательское промежуточное программное обеспечение для обработки всех ошибок, связанных с api. В этом случае ваш код будет более чистым.

   failure/ error actin type ACTION_ERROR

   export default  (state) => (next) => (action) => {

      if(ACTION_ERROR.contains('_ERROR')){

       // fire error action
        store.dispatch(serviceError());

       }
}

что я делаю, так это централизую всю обработку ошибок в эффекте на основе каждого эффекта

/**
 * central error handling
 */
@Effect({dispatch: false})
httpErrors$: Observable<any> = this.actions$
    .ofType(
        EHitCountsActions.HitCountsError
    ).map(payload => payload)
    .switchMap(error => {
        return of(confirm(`There was an error accessing the server: ${error}`));
    });

можно использовать HTTP-клиент axios. Он уже реализовал функцию перехватчиков. Вы можете перехватывать запросы или ответы до того, как они будут обработаны then или catch.

https://github.com/mzabriskie/axios#interceptors

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });