Каков наилучший способ справиться с ошибкой выборки в реакции редукции?
У меня есть один редуктор для клиентов, еще один для AppToolbar и некоторых других...
Теперь скажем, что я создал действие для извлечения, чтобы удалить клиента, и если он сбой, у меня есть код в редиректоре клиентов, который должен сделать некоторые вещи, но также я хочу отобразить некоторую глобальную ошибку в AppToolbar.
Но клиенты и адаптеры AppToolbar не разделяют одну и ту же часть состояния, и я не могу создать новое действие в редукторе.
Итак, как я могу представить глобальную ошибку? Благодаря
ОБНОВЛЕНИЕ 1:
Я забыл упомянуть, что использую este devstack
ОБНОВЛЕНИЕ 2:
Я заметил, что Эрик ответил правильно, но я должен сказать, что решение, которое я использую в este, больше похоже на сочетание ответов Эрика и Дэна...
Вам просто нужно найти то, что вам подходит лучше всего в вашем коде...
Ответы
Ответ 1
Если вы хотите иметь концепцию "глобальных ошибок", вы можете создать редуктор errors
, который может прослушивать действия addError, removeError и т.д. Затем вы можете подключиться к дереву состояний Redux в state.errors
и отображать их там, где это необходимо.
Существует несколько способов, с помощью которых вы могли бы приблизиться к этому, но общая идея состоит в том, что глобальные ошибки/сообщения заслуживают собственного редуктора, чтобы жить полностью отдельно от <Clients />
/<AppToolbar />
. Конечно, если любой из этих компонентов нуждается в доступе к errors
, вы можете передать errors
вниз к ним в качестве прокрутки там, где это необходимо.
Обновление: пример кода
Вот один пример того, как это могло бы выглядеть, если бы вы передали "глобальные ошибки" errors
на ваш верхний уровень <App />
и условно визуализировали его (если есть ошибки). Используя react-redux connect
, подключите ваш компонент <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;
}
}
Ответ 2
Ответ Eriks является правильным, но я хотел бы добавить, что вам не нужно запускать отдельные действия для добавления ошибок. Альтернативный подход состоит в том, чтобы иметь редуктор, который обрабатывает любое действие с полем error
. Это вопрос личного выбора и соглашения.
Например, из Redux 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
}
Ответ 3
Подход, который я использую в настоящее время для нескольких конкретных ошибок (проверка правильности ввода), заключается в том, чтобы мои вспомогательные редукторы генерировали исключение, улавливали его в моем корневом редукторе и привязывали его к объекту действия. Затем у меня есть сага о сокращении, которая проверяет объекты действия для ошибки и обновляет дерево состояний с данными об ошибках в этом случае.
Итак:
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));
}
}
И затем добавление ошибки в дерево состояний, как описывает Эрик.
Я использую его довольно экономно, но это не позволяет мне дублировать логику, которая законно принадлежит редуктору (поэтому она может защитить себя от недопустимого состояния).
Ответ 4
Вы можете использовать HTTP-клиент axios. Он уже реализовал функцию Interceptors. Вы можете перехватывать запросы или ответы до того, как они будут обработаны тогда или поймать.
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);
});