Подтвердить что ты не робот

Как сделать глобальный обработчик ошибок в Redux и переопределить его, когда это необходимо?

Представьте себе ситуацию с обработкой формы submit, которая может возвращать различные ошибки: 400, 401, 500. Когда возвращается 400, я хочу показать сообщение в верхней части формы (переопределить поведение по умолчанию). Для других (необработанных) кодов ошибок должен быть вызван обработчик ошибок по умолчанию (глобальный) (который показывает уведомление об атаке). Просто не хотите дублировать этот код для каждого отдельного действия.

Я отправляю асинхронные действия с использованием промежуточного ПО relx-thunk

// Pseudo code
const action = (dispatch) => {

    const onSuccess = (result) => dispatch({type: 'OPERATION_SUCCESS', payload: result});
    const onError = (error) => dispatch({type: 'OPERATION_ERROR', error: true, payload: error});

    return promise.then(onSuccess, onError);

};
dispatch(action);

Я могу создать редуктор, который обрабатывает все действия {error: true} и отображает всплывающее уведомление (возможно, без использования состояния redux, непосредственно вызывающего метод toast.show()) Но как определить, была ли эта специальная ошибка обработана каким-либо другим редуктором?

4b9b3361

Ответ 1

Когда действие достигнет редуктора, это факт. Он отражает то, что уже произошло. Нет смысла спрашивать "имеет ли другой редуктор это действие?" потому что редукторы должны быть пассивными и, в общем, не знать о существовании друг друга. Они должны стремиться быть независимыми, где это возможно.

Нет никакого "истинного" способа выполнить то, что вы хотели, но так как вы уже используете соглашение об обработке любого объекта с свойством error как глобальную ошибку, вы можете также ввести другое соглашение, например: "если действие имеет флаг suppressGlobalErrorNotification, тогда глобальный редуктор ошибок не должен заботиться об этом".

// utilities

function checkStatus(response) {
  if (response.status >= 200 && response.status < 300) {
    return response
  } else {
    const error = new Error(response.statusText)
    error.response = response
    throw error
  }
}

function parseJSON(response) {
  return response.json()
}

export function post(url, data) {
  const options = {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)  
  }
  return fetch(url, options)
    .then(checkStatus)
    .then(parseJSON)
}

// action creators

import { post } from './utils'

export function submitForm(data) {
  return dispatch => post('/myform', data).then(
    response => dispatch({
      type: 'SUBMIT_FORM_SUCCESS',
      payload: response
    }),
    error => dispatch({
      type: 'SUBMIT_FORM_FAILURE',
      error: error,
      suppressGlobalErrorNotification: (
        error.response &&
        error.response.status === 400
      )
    })
  )
}

// reducers

export function error(state = null, action) {
  if (!action.error || action.suppressGlobalErrorNotification) {
    return state
  }
  if (action.type === 'RESET_ERROR') {
    return null
  }
  return action.error
}


export function form(state = {}, action) {
  switch (action.type) {
  case 'SUBMIT_FORM_FAILURE':
    return Object.assign({}, state, { isFormError: true })
  // ...
  default:
    return state
  }
}

Ответ 2

То же самое для меня, я не нашел никакого рабочего решения, кроме как справиться с этим. @Dan Abramov показал пример, но проблема здесь в том, что когда у вас есть десятки форм, вещи становятся более сложными. Каждый раз, когда вам нужно обрабатывать одинаковые вещи, дублирующий код начинает раздражать. Например:

form => client validation => CLIENT_VALIDATION_ERROR => reducer

                       FETCH_STARTED
form => form submit => SERVER_SIDE_SUCCESS  => reducers
                       SERVER_SIDE_ERROR

Может быть исключение, где нам нужно обрабатывать такое поведение вручную, но в большинстве случаев это не так.