快速学会使用react redux
这里以在react当中的使用为例。
搭配 React Router
一般都会使用react router ,以便于路由处理器可以访问 store。尤其是在需要时光旅行和回放 action 来触发 URL 改变的需求下。
从 React Redux 导入 Provider : import { Provider } from 'react-redux';
<Provider />
是由 React Redux 提供的高阶组件,用来让你将 Redux 绑定到 React,将用 <Provider />
包裹 <Router />
hash 路由
const Root = ({ store }) => (
<Provider store={store}>
<Router>
<Route path="/" component={App} />
</Router>
</Provider>
);
不使用 hash 路由
import React, { PropTypes } from 'react';
import { Provider } from 'react-redux';
import { Router, Route, browserHistory } from 'react-router';
import App from './App';
const Root = ({ store }) => (
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/(:filter)" component={App} />
</Router>
</Provider>
);
Root.propTypes = {
store: PropTypes.object.isRequired,
};
export default Root;
使用 typesafe-actions
使用typesafe-actions 将action生成和reducer使用更加简便。
使用typesafe-actions,我们一般会将 store
文件夹中加入四个文件。
分别是xxxAction.ts 、xxxEpic.ts、xxxReducer.ts 、xxxxSelector.ts
xxxAction.ts
在这个文件中,定义所有的动作和动作的输入参数。
import { createAction } from 'typesafe-actions';
import { LessonInfo } from '../LessonDetailType';
export const GetLessonDetailAction = createAction('[LessonDetail] start get list', (id: number) => ({ id }))();
export const GetLessonDetailSuccessAction = createAction(
'[LessonDetail] get list success',
(lessonInfo: LessonInfo) => ({
lessonInfo,
}),
)();
export const GetLessonDetailFailAction = createAction('[LessonDetail] get list failed', (errorText: string) => ({
errorText,
}))();
export const GetLessonDetailResetAction = createAction('[LessonDetail] rest list')();
其中我们会用到 createAction,这是一个高效的无限参数的 action 生成器
参数: (id, title, amount, etc...)
返回: ({ type, payload, meta }) action 对象。
在这里,第一个参数用来描述这个动作,第二个参数描述了该动作的输入参数。
我们在组件或者页面的代码中,使用useDispatch去触发action
import { useDispatch, useSelector } from 'react-redux';
const dispatch = useDispatch();
useEffect(() => {
dispatch(GetLessonDetailAction(id));
return () => {
dispatch(GetLessonDetailResetAction());
};
}, [dispatch, id]);
xxxEpic.ts
当一个 action的动作结果有两种状态或多种状态时,我们就需要将action分开,并在epic中定义action的前后逻辑关系。
举例:当我们向服务端进行请求课程列表,就会有请求成功返回课程列表和请求失败什么数据都没有的两种结果。这时候,我们一般会定义三个action,即 GetLessonDetailAction
、GetLessonDetailSuccessAction
、GetLessonDetailFailAction
。
当我们在代码中触发请求的动作时,即 dispatch(GetLessonDetailAction(id))
,请求的成功或失败就会导致最后使用的state出现两种状态。所以我们应该根据 GetLessonDetailAction(id)
的结果去分别触发 GetLessonDetailSuccessAction
、GetLessonDetailFailAction
。
import { from, of } from 'rxjs';
import { switchMap, catchError, filter, map } from 'rxjs/operators';
import { Epic } from 'redux-observable-es6-compat';
import { RootAction, RootState, isActionOf } from 'typesafe-actions';
import { GetLessonDetailAction, GetLessonDetailSuccessAction, GetLessonDetailFailAction } from './LessonDetailAction';
import { container } from 'tsyringe';
import { LessonDetailService } from '../LessonDetailService';
import { LessonInfo } from '../LessonDetailType';
const lessonDetailService = container.resolve(LessonDetailService);
const GetLessonDetailEpic: Epic<RootAction, RootAction, RootState> = action$ =>
action$.pipe(
filter(isActionOf(GetLessonDetailAction)),
switchMap(action => {
return from(lessonDetailService.getLessonDetail(action.payload.id)).pipe(
map((lessonInfo: LessonInfo) => {
return GetLessonDetailSuccessAction(lessonInfo);
}),
catchError(err => {
return of(GetLessonDetailFailAction('something wrong'));
}),
);
}),
);
export { GetLessonDetailEpic };
当有dispatch动作时,就会进入epic对触发action进行识别和处理。
我们只需要在epic中写好识别action及其action的处理方式。
xxxReducer.ts
reducer文件是对state改变前后和数据结构的管理和定义。
在reducer文件中,我们需要先定义好defaultState的默认状态,然后对于有对state进行改变的action,handleType的第二个参数(state, action) => {return {...state,}},state是当前state的数值,action是对于action完成后的action对象,其中action.payload中包含着我们在xxxaction.ts 文件中的设置的payload,函数的返回是更新后的state。
import { createReducer, getType, RootAction } from 'typesafe-actions';
import {
GetLessonDetailAction,
GetLessonDetailSuccessAction,
GetLessonDetailFailAction,
GetLessonDetailResetAction,
} from './LessonDetailAction';
import { LessonDetail } from '../LessonDetailType';
const defaultState: LessonDetail = {
id: 0,
lessonName: '',
duration: '',
teacher: null,
episodes: [],
paid: false,
product: null,
};
const changeFigureToChinese = (day: number): string => {
switch (day) {
case 0:
return '一';
case 1:
return '二';
case 2:
return '三';
case 3:
return '四';
case 4:
return '五';
case 5:
return '六';
case 6:
return '日';
default:
return 'x';
}
};
export default createReducer<LessonDetail, RootAction>(defaultState)
.handleType(getType(GetLessonDetailAction), (state, action) => {
return {
...state,
id: action.payload.id,
};
})
.handleType(getType(GetLessonDetailSuccessAction), (state, action) => {
const lessonInfo = action.payload.lessonInfo;
if (lessonInfo) {
const startDate = new Date(lessonInfo.lesson.startTime);
const endDate = new Date(lessonInfo.lesson.endTime);
const episodes = lessonInfo.lessonAgendas.map(({ id, name, startTime, endTime }) => {
const episodeStartTime = new Date(startTime);
const episodeEndTime = new Date(endTime);
return {
id,
name,
duration: `${episodeStartTime.getMonth() + 1}月${episodeStartTime.getDate()}日 周${changeFigureToChinese(
episodeStartTime.getDay(),
)} ${episodeStartTime.getHours() + 1}:${episodeStartTime.getMinutes()}-${
episodeEndTime.getHours() + 1
}:${episodeEndTime.getMinutes()} `,
};
});
return {
...state,
id: lessonInfo.lesson.id,
lessonName: lessonInfo.lesson.name,
duration: `${startDate.getMonth() + 1}月${startDate.getDate()}日 - ${
endDate.getMonth() + 1
}月${endDate.getDate()}日 `,
teacher: lessonInfo.lesson.teacher[0],
episodes,
paid: lessonInfo.paid,
product: lessonInfo.product,
};
} else {
return {
...state,
};
}
})
.handleType(getType(GetLessonDetailFailAction), (state, action) => {
return { ...state };
})
.handleType(getType(GetLessonDetailResetAction), (state, action) => defaultState);
xxxxSelector.ts
selector的作用是方便我们直接使用state中的内容。
我们需要先在全局的 store
中,将上述reducer引入
import { combineReducers } from 'redux';
import login from '@/containers/account-content/store/LoginReducer';
import courseList from '@/containers/home-course-list/store/CourseListReducer';
import lessonDetail from '@/containers/lesson-detail/store/LessonDetailReducer';
const rootReducer = combineReducers({
login,
courseList,
lessonDetail,
});
export default rootReducer;
然后就可以在 xxxxSelector.ts中使用
import { createSelector } from 'reselect';
import { RootState } from 'typesafe-actions';
import { LessonDetail } from '../LessonDetailType';
export const selectLessonDetailState = createSelector(
(state: RootState) => state.lessonDetail,
(lessonDetail: LessonDetail) => lessonDetail,
);
然后我们就可以在代码中直接取出我们想要的值了。
import { useDispatch, useSelector } from 'react-redux';
import { selectLessonDetailState } from './store/LessonDetailSelector';
const { lessonName, duration, teacher, episodes, paid, product } = useSelector(selectLessonDetailState);
标题:快速学会使用react redux
作者:limanting
地址:https://blog.manxiaozhi.com/articles/2021/08/15/1629039979866.html