Redux + TypeScript最佳实践

这里简单的介绍了redux结合typescript的使用技巧,方便在开发的时候,利用typescript的特点,对数据类型进行校验和提示

store

创建 store文件夹

创建 store/index.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { createStore, Store } from "redux";
import { create } from "redux-react-hook";

import reducers, { Action } from "./reducers";

export type RootState = ReturnType<typeof reducers>;

export function makeStore(): Store<RootState, Action> {
return createStore(reducers);
}

export const { StoreContext, useDispatch, useMappedState } = create<
RootState,
Action,
Store<RootState, Action>
>();

创建 store/reducers文件夹

创建 store/reducers/index.ts

1
2
3
4
5
6
7
8
9
10
11
import { combineReducers } from "redux";

import common, { Action as Action1 } from "./common";
import app, { Action as Action2 } from "./app";

export default combineReducers({
common,
app,
});

export type Action = Action1 | Action2;

创建 store/reducers/common.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { produce } from "immer";

export interface State {
routerChanged: boolean;
}

export interface Action {
type: "CM:router changed";
routerChanged: boolean;
}

const INITIAL_STATE: State = {
routerChanged: false,
};

export default function reducer(state: State = INITIAL_STATE, action: Action) {
return produce(state, (draft) => {
switch (action.type) {
case "CM:router changed": {
draft.routerChanged = action.routerChanged;
break;
}

default:
return state;
}
});
}

创建 store/reducers/app.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { produce } from "immer";

export interface State {
addr: string;
}

export interface Action {
type: "TM:add addr";
addr: string;
}

const INITIAL_STATE: State = {
addr: "",
};

export default function reducer(state: State = INITIAL_STATE, action: Action) {
return produce(state, (draft) => {
switch (action.type) {
case "TM:add addr": {
draft.addr = action.addr;
break;
}

default:
return state;
}
});
}

使用

入口组件 index.ts - 注册 store

1
2
3
4
5
6
7
8
9
10
11
12
import * as React from "react";
import { makeStore, StoreContext } from "@/store";

const store = makeStore();

export default () => {
return (
<StoreContext.Provider value={store}>
<App />
</StoreContext.Provider>
);
};

使用app reducer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import * as React from "react";
import { useDispatch, useMappedState } from "@/store";

export default () => {
const dispatch = useDispatch();
// 使用了该TS类型能力,这里的state都会有字段提示了
const addr = useMappedState((state) => state.app.addr);

return (
<div>
<h1>{addr}</h1>
<button
onClick={() => {
// 同时这里的disptach也会有相应的type和其他字段的提示
dispatch({
type: "TM:add addr",
addr: String(Math.random()),
});
}}
>
修改
</button>
</div>
);
};

说明

在定义 dispatch 的 type 名字时,最好能提供丰富的名字说明,且添加前缀标识,这样方便识别和使用