Каков наилучший способ справиться с ошибкой выборки в реагировать на редуксы?
У меня есть один редуктор для клиентов, другой для AppToolbar и некоторые другие...
Теперь допустим, что я создал действие извлечения для удаления клиента, и в случае сбоя у меня есть код в редукторе клиентов, который должен что-то делать, но я также хочу отобразить глобальную ошибку в AppToolbar.
Но клиенты и редукторы AppToolbar не разделяют одну и ту же часть состояния, и я не могу создать новое действие в редукторе.
Так как же мне показать глобальную ошибку? Спасибо
ОБНОВЛЕНИЕ 1:
Я забыл упомянуть, что я использую este Devstack
ОБНОВЛЕНИЕ 2: Я отметил ответ Эрика как правильный, но я должен сказать, что решение, которое я использую в este, больше похоже на комбинацию ответа Эрика и Дэна... Вам просто нужно найти то, что подходит вам лучше всего в вашем коде...,
6 ответов
Если вы хотите иметь понятие "глобальные ошибки", вы можете создать errors
редуктор, который может прослушивать действия addError, removeError и т. д. Затем вы можете подключиться к дереву состояний Redux по адресу state.errors
и отображать их, где это уместно.
Есть несколько способов, которыми вы могли бы подойти к этому, но общая идея состоит в том, что глобальные ошибки / сообщения заслуживают того, чтобы их собственный редуктор жил полностью отдельно от <Clients />
/<AppToolbar />
, Конечно, если любому из этих компонентов нужен доступ к errors
ты мог пройти errors
до них, как опора, где это необходимо.
Обновление: Пример кода
Вот один пример того, как это могло бы выглядеть, если бы вы передавали "глобальные ошибки" errors
в ваш верхний уровень <App />
и условно отрендерить его (если присутствуют ошибки). Использование реаги-редукса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);
А что касается создателя действия, то он будет отправлять ( избыточно-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;
}
}
Эрик ответил правильно, но я хотел бы добавить, что вам не нужно запускать отдельные действия для добавления ошибок. Альтернативный подход заключается в том, чтобы иметь редуктор, который обрабатывает любое действие с 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 error
}
return state
}
Подход, который я сейчас использую для нескольких конкретных ошибок (проверка пользовательского ввода), заключается в том, чтобы мои суб-редукторы генерировали исключение, перехватывали его в моем корневом редукторе и прикрепляли его к объекту действия. Затем у меня есть Redx-сага, которая проверяет объекты действий на наличие ошибок и обновляет дерево состояний данными об ошибках в этом случае.
Так:
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));
}
}
А затем добавление ошибки в дерево состояний происходит так, как описывает Эрик.
Я использую его довольно редко, но это избавляет меня от необходимости дублировать логику, которая на законных основаниях принадлежит редуктору (чтобы он мог защитить себя от недопустимого состояния).
Написать собственное Middleware для обработки всех ошибок, связанных с 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}`));
});
Попробуйте этот метод.
Он получит ответ от API, который поможет нам получить статус
import {
FETCH_POSTS,
NEW_POSTS,
NEW_POSTS_ERROR,
FETCH_POSTS_ERROR,
} from "../actions/types";
const initialState = {
items: [],
item: {},
error: {},
isLoading: true,
};
export default function (state = initialState, action) {
switch (action.type) {
case FETCH_POSTS_ERROR:
case NEW_POSTS_ERROR:
return {
...state,
items: null,
item: null,
error: action.payload,
isLoading: false,
};
case FETCH_POSTS:
return {
...state,
items: action.payload,
isLoading: true,
};
case NEW_POSTS:
return {
...state,
item: action.payload,
isLoading: true,
};
default:
return state;
}
}
Вы можете использовать Axios HTTP-клиент. Он уже реализовал функцию перехватчиков. Вы можете перехватывать запросы или ответы до того, как они будут обработаны, или перехватить.
https://github.com/mzabriskie/axios
// 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);
});