目录
1. 安装Redux基本环境
2. 为项目引入Redux
2.1 rootReducer的创建
2.2 生成store
2.3 包裹根组件
3. 子模块中使用Redux
3.1 创建types文件
3.2 回顾一下Redux的工作流
3.3 分别创建子模块的reducer和actions
reducer
actions
3.4 子模块主文件的编写
react-redux的映射函数
渲染列表
新增一个Card
注意点
4. 参考资料
目前的项目是基于Typescript下的React,接入Redux的话在部分接口声明和定义的地方是需要注意的,这里整理一下步骤并进行记录。
我们目前需要使用的Redux相关库包括redux、react-redux、redux-saga,所以首先需要做的是通过npm或者yarn安装对应的库,注意这其中react-redux需要加上@types的前缀才行:
npm install --save @types/react-redux
这部分和是否TS环境下安装过程基本一致,只是注意有一些库需不需要@types声明就行了。
接着我们需要为我们的项目引入Redux,如果你之前从未使用过Redux,建议你先看看文章了解一下Redux的基本使用,
如果你已经了解了那么下面我就重点从需要注意的地方入手,这一章节只说一下Redux基础的文件创建过程。
首先我们需要创建一个根Reducer,这个Reducer是通过combineReducers方法将各个子模块的Reducer进行整合(这部分其实和TS无关):
src/redux/reducers.tsx
import {combineReducers} from 'redux';
import {fileManagerReducer} from "../modules/workspace/containers/FileManager/reducer";
const rootReducer = combineReducers({
fileManager: fileManagerReducer
});
export default rootReducer;
export type RootState = ReturnType;
这里我们只写了一个子模块作为示例。
这里注意我们需要export的包括rootReducer还有一个类型:RootState,因为后面在进行mapStateToProps的过程中我们需要给传入的state声明一个类型,而这个RootState就是他的类型!
这部分跟TS关系也不大,我就直接贴代码了:
src/redux/index.tsx
import createSagaMiddleware from "redux-saga";
import {applyMiddleware, createStore, compose} from "redux";
import rootReducer from "./rootReducer";
import myMiddleware from "./middleware";
//添加中间件和调试工具
const sagaMiddleware = createSagaMiddleware();
const composeEnhancers =
typeof window === 'object' &&
(window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
(window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(myMiddleware, sagaMiddleware)));
// sagaMiddleware.run(mainSaga);
export default store;
我这边使用了中间件和Redux调试工具所以createStore多了一些参数。
接着需要把根组件进行包裹,让其中的组件都可以使用Redux,这个是react-redux库的功能:
src/index.tsx
import React from "react";
import ReactDOM from "react-dom";
import {Provider} from 'react-redux';
import MainFrame from './components/MainFrame';
import store from "./redux";
ReactDOM.render(
,
document.getElementById('root') as HTMLElement
);
通过以上步骤就为我们的项目引入了Redux了,而下面我将具体举一个实例来分析针对某一个具体模块使用redux的步骤和注意事项。
接着我们需要在一个列表子模块中实现这样一个功能,列表可以渲染state中的一个数组,同时列表中需要提供一个新增按钮来新增一个测试数据,同时实时更新当前列表。
首先我们需要创建一个types文件,这个文件主要定义这个模块中使用到的state和action的接口类型,这也是TS中的特色之一:
//State
export interface CardFile {
id: string
title: string
creator: string
desc: string
}
export interface FileManagerState {
cardFiles: CardFile[]
selectCardId: string
}
//Action
export const MAKE_NEW_CARD = 'MAKE_NEW_CARD';
export const SELECT_CARD = 'SELECT_CARD';
interface MakeNewCardAction {
type: typeof MAKE_NEW_CARD
payload: CardFile
}
interface SelectCardAction {
type: typeof SELECT_CARD
selectCardId: number | string
}
export type FileManagerActionTypes = MakeNewCardAction | SelectCardAction;
这里如果有多个Action,最后声明的Types就用|来表示。
接着我们需要根据Redux的工作流来分别定义Action和Reducer了,这里我们利用下面这张图简单回顾一下吧:
---图片系统抽风了,后面能显示的话我贴下图
如果一个组件需要读取Store中存储的数据,那么他需要获取store并getState来获取其中的数据。
如果一个组件需要修改Store中存储的数据,那么他需要创建一个Action并Disptach出去,传递到Reducer生成一个新的State从而重新渲染原先的界面。
这里具体详细的步骤就不展开说了,不清楚的可以再去Google一下
src/modules/workspace/containers/FileManager/reducer.tsx
import {FileManagerActionTypes, FileManagerState, MAKE_NEW_CARD, SELECT_CARD} from './types'
const initialState: FileManagerState = {
cardFiles: [
{
id: '100',
title: 'CardNo1',
creator: 'Joern',
desc: '空'
},
{
id: '101',
title: 'CardNo2',
creator: 'Joern',
desc: '空'
},
{
id: '102',
title: 'CardNo3',
creator: 'Joern',
desc: '空'
},
],
selectCardId: '100'
};
export function fileManagerReducer(
state = initialState,
action: FileManagerActionTypes
): FileManagerState {
switch (action.type) {
case MAKE_NEW_CARD:
return Object.assign({}, state, {
cardFiles: [
...state.cardFiles,
action.payload
]
});
case SELECT_CARD:
console.log(action.selectCardId);
return Object.assign({}, state, {
selectCardId: action.selectCardId
});
default:
return state
}
}
因为使用了TS,所以对应的state和action都需要进行类型声明,这里的类型就是我们之前在types文件里面声明好的,这里直接使用就可以了。
这里我们关注Reducer接受到一个type为MAKE_NEW_CARD类型的Action时,他会在原先State基础上更新cardFiles对象,将action的payload传入生成一个新的数组。
src/modules/workspace/containers/FileManager/actions.tsx
import {CardFile, FileManagerActionTypes, MAKE_NEW_CARD, SELECT_CARD} from './types'
export function makeNewCard(newCard: CardFile): FileManagerActionTypes {
return {
type: MAKE_NEW_CARD,
payload: newCard
}
}
export function selectCard(selectedCardId: number | string): FileManagerActionTypes {
return {
type: SELECT_CARD,
selectCardId: selectedCardId
}
}
这里就是定义了两个Action,注意需要声明好Action的类型,并且传参的使用也需要和对应的字段类型匹配。
我们的主文件是index文件,在这个文件里面我们需要处理读取state和新增发送action的逻辑,我们来看一下。
如果基于react-redux库我们知道需要定义两个映射的函数,反别将State映射到Props,将Action插入到Props:
// 将reducer中的状态插入到组件的 props 中
const mapStateToProps = (state: RootState) => {
const {fileManager} = state;
return {
cardFiles: fileManager.cardFiles,
selectCardId: fileManager.selectCardId
}
};
// 将对应action 插入到组件的props中
const mapDispatchToProps = (dispatch: Dispatch) => {
return {
handleItemClick: (index: number | string) => {
dispatch(selectCard(index));
},
makeNewCard: (newCard: CardFile) => {
dispatch(makeNewCard(newCard));
}
}
};
这里有一个需要注意的地方,如果你使用的Reducer是基于combine来进行组合的,那么你的state的接口定义的是全局combine之后的Reducer,你需要获取具体的子Reducer才可以成功获取其中的state!
好了接着可以开始写TSX逻辑了,记得组件的Props先需要声明哈:
interface IProps {
cardFiles: CardFile[];
handleItemClick: (index: number | string) => void
makeNewCard: (card: CardFile) => void
}
//测试数据-添加用的
const testNewCard: CardFile = {
creator: "New", desc: "111", id: "98", title: "NewCard"
};
class FileManager extends Component {...}
我们先实现拿到cardFiles来渲染List吧,这里直接贴代码:
class FileManager extends Component {
public render() {
const {cardFiles, handleItemClick, makeNewCard} = this.props;
console.log(this.props);
return (
....
(
// Antd的默认间距太大
handleItemClick(item.id)}>
}
title={
}
/>
)}
/>
)
}
}
直接通过this.props.cardFiles拿到就可以使用了,注意List中的item类型就是一个cardFile,如果使用IDE你会发现会有代码提示补齐,这也是使用TS的好处之一~
接着我们希望新增一个Card并实时渲染出来,我们直接在TSX中嵌入这么一段:
这样就可以了,点击之后就会生成对应的action并传递给reducer进行cardFiles的新增,之后state会出现差异,于是对应的列表就会重新渲染拉。
这里还有一个注意事项就是onClick这些事件传递函数的不能这样写,会报错的:(图片上传不了,不知道为啥,其实就是你直接调用函数的话会报错,如果传递函数需要在TSX里面入参的话就要用箭头函数构造一遍!)
主要是因为返回类型的校验
有误,需要构造一个箭头函数解决!
以上基本就完成了一个基于React在TS环境下的Redux工作流的搭建工作了~
这里贴出一下我参考和解决问题的链接,供大家深入学习:
合理布置Redux目录:https://juejin.im/post/58cbfcb05c497d0057b9b228
官方Redux在TS中使用:https://redux.js.org/recipes/usage-with-typescript
TypeScript + React + Redux 的个人总结:https://www.jianshu.com/p/2b981304cdd4
React And TypeScript(二:集成Redux):https://blog.csdn.net/u010377383/article/details/79022439
Reducer详述:https://cn.redux.js.org/docs/basics/Reducers.html
React状态管理框架Redux使用:https://juejin.im/entry/5d4be934e51d456210163b53