Я учусь реагировать с редуксом и наблюдаемым редуксом. В частности, я пытаюсь понять, как подходить к обработке ошибок. До сих пор:
Мне удалось создать эпопею, наблюдаемую редуксом, которая реагирует на действие извлечения. В частности, он делает запрос ajax и отвечает успешным или неудачным действием.
a. Если запрос ajax завершается успешно, то эпопея запускает успешное действие с данными ответа в качестве полезной нагрузки.
b. Если запрос терпит неудачу, то эпопея инициирует действие сбоя с ошибкой в качестве полезной нагрузки.
Компонент проверяет наличие состояния ошибки и, если он присутствует, затем выдает егоокружающий компонент Граница ошибки.
Проблема, с которой я сталкиваюсь при таком подходе, заключается в том, что ошибка остается в состоянии сохранения. В качестве примера предположим следующую локальную среду разработки:
- React / Redux клиент с использованием наблюдаемых при редексах эпосов для выполнения http-запросов.
- Сервер API отдыха ( НЕ НАЧИНАЕТСЯ )
Когда мой клиент React / Redux делает запрос серверу Rest API, возникает ошибка сетевого подключения, запускающая действие сбоя и приводящая к сохранению состояния ошибки. Затем я START сервер API отдыха и пытаюсь сделать тот же HTTP-запрос от клиента React / Redux. Ошибка из предыдущего запроса остается в магазине. Следовательно, отображается сообщение об ошибке.
Впоследствии, как мне выполнить сброс, чтобы разрешить новый запрос без ошибок? Существуют ли рекомендуемые шаблоны для обработки ошибок с реакцией и редукцией при использовании промежуточного программного обеспечения, такого как наблюдаемое в редуксе или саге на редуксе?
Epic
import { Epic } from 'redux-observable';
import { isActionOf } from 'typesafe-actions';
import { of } from 'rxjs';
import { catchError, filter, map, switchMap } from 'rxjs/operators';
import { fetchCoursesAsync } from './actions';
import { RootAction, RootState, Services } from 'ReduxTypes';
export const fetchCoursesRequestAction: Epic<
RootAction,
RootAction,
RootState,
Services
> = (action$, state$, { courseServices }) =>
action$.pipe(
filter(isActionOf(fetchCoursesAsync.request)),
switchMap(() =>
courseServices.default.getCourses().pipe(
map(fetchCoursesAsync.success),
catchError((error: Error) =>
of(fetchCoursesAsync.failure({ hasError: true, error: error })),
),
),
),
);
Функциональная составляющая
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import Grid from '@material-ui/core/Grid';
import { GridSpacing } from '@material-ui/core/Grid';
import Course from '../components/Course/Course';
import { courseModels } from '../redux/features/course';
import { courseSelectors } from '../redux/features/course';
import { fetchCoursesAsync } from '../redux/features/course/actions';
import { RootState } from 'ReduxTypes';
type ErrorReport = { hasError: boolean; error?: Error };
type StateProps = {
isLoading: boolean;
courses: courseModels.Course[];
error: ErrorReport;
};
/**
* Redix state and dispatch mappings
*/
const dispatchProps = {
fetchCourses: fetchCoursesAsync.request,
};
const mapStateToProps = (state: RootState): StateProps => ({
isLoading: state.courses.isLoadingCourses,
courses: courseSelectors.getReduxCourses(state.courses),
error: courseSelectors.getReduxCoursesError(state.courses),
});
/**
* Component property type definitions
*/
type Props = ReturnType<typeof mapStateToProps> & typeof dispatchProps;
/**
* CourseList component
*/
const CourseList = ({
courses = [],
error,
fetchCourses,
isLoading,
}: Props): JSX.Element => {
// fetch course action on mount
useEffect(() => {
fetchCourses();
}, []);
if (isLoading) {
return <p>Loading...</p>;
}
if (error && error.hasError && error.error) {
throw error.error; // notify surrounding Error Boundary
}
return (
<div style={{ marginTop: 20, padding: 30 }}>
{
<Grid container spacing={2 as GridSpacing} justify="center">
{courses.map(element => (
<Grid item key={element.courseID}>
<Course course={element} />
</Grid>
))}
</Grid>
}
</div>
);
};
/**
* Exports
*/
export default connect(
mapStateToProps,
dispatchProps,
)(CourseList);