我正在学习对redux和redux-observable的反应。特别是,我试图了解如何进行错误处理。到目前为止:
我设法创建了一个可观察到的Redux的史诗,该史诗可响应提取操作。具体来说,它发出一个ajax请求,并以成功或失败操作作为响应。
a。如果ajax请求成功,那么该事件将以响应数据作为有效负载触发成功动作。
b。如果请求失败,则该史诗会触发失败操作,并以错误作为有效负载。
该组件检查是否存在错误状态,如果存在,则将其抛出到周围的错误边界组件。
我使用此方法遇到的问题是,错误仍保留在存储状态中。例如,假设以下本地开发环境:
当我的React / Redux客户端向Rest API服务器发出请求时,会引发网络连接错误,触发故障操作并导致错误状态被存储。然后,我START Rest API服务器,并尝试从React / Redux客户端发出相同的http请求。来自上一个请求的错误仍保留在存储中。因此,将显示错误通知。
随后,如何重置以允许无错误的全新请求?使用中间件(例如redux-observable或redux-saga)时,是否有任何建议的模式用于使用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);
认为通过提出新的获取请求或获取成功时,清除与获取相关的错误状态,已经解决了根本问题。我在下面包括了reducer代码和客户端组件代码。接受其他建议。...
如果客户端组件检测到错误状态,则将其呈现。到目前为止,我发现,如果客户端组件抛出错误(由错误状态封装),则会被周围的错误边界捕获。但是,下次访问该组件时,在useEffect函数中似乎未引发提取动作。我将不得不对此进行进一步调查,以确定问题是否在我的ErrorBoundary组件内。...也许是随后的单独问题。
减速器
import { combineReducers } from 'redux';
import { createReducer } from 'typesafe-actions';
import { fetchCoursesAsync } from './actions';
import { Course } from './model';
/**
* Error State
*/
type ErrorReport = Readonly<{
hasError: boolean;
error: undefined;
}>;
const initialErrorState: ErrorReport = {
hasError: false,
error: undefined,
};
const isLoadingCourses = createReducer(false as boolean)
.handleAction([fetchCoursesAsync.request], () => true)
.handleAction(
[fetchCoursesAsync.success, fetchCoursesAsync.failure],
() => false,
);
const courses = createReducer([] as Course[]).handleAction(
fetchCoursesAsync.success,
(state, action) => action.payload,
);
const error = createReducer(initialErrorState)
.handleAction(
[fetchCoursesAsync.request, fetchCoursesAsync.success],
(state): ErrorReport => ({
hasError: false,
error: undefined,
}),
)
.handleAction(
[fetchCoursesAsync.failure],
(state, action): ErrorReport => ({
hasError: action.payload.hasError,
error: action.payload.error,
}),
);
const coursesReducer = combineReducers({
isLoadingCourses,
courses,
error,
});
export default coursesReducer;
export type CoursesState = ReturnType<typeof coursesReducer>;
CourseList
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;
};
/**
* Redux dispatch and state 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(() => {
console.log('COURSELIST FETCHING COURSES');
fetchCourses();
}, [fetchCourses]);
if (isLoading) {
return <p>Loading...</p>;
}
if (error && error.hasError && error.error) {
// throw error.error;
// if throw the error from state then encapsulating error boundary catches and displays
// however subsequent request actions are not processed by redux-observable action stream
// needs further investigation and if necessary a subsequent question posted....
// if render error inside component then redux-observable stream continues
// to process subsequent actions.
return <p>{JSON.stringify(error.error, null, 2)}</p>;
}
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);