在上一篇文章中,我们在项目中集成了的同步 Redux.在本篇文章中,我们将在 Redux 中使用异步 actions 优化我们的代码
State
我们先来回忆下我们的 store 类型
type Person = {
id: number;
name: string;
};
type AppState = {
people: Person[];
};
so? 我们的 store 中包含一个 people 字段,它是一个包含 id/name 的数组类型
我们只需要定义一些较简洁的 store 状态,以便我们专注于 Redux Store 以及 React 组件如何以强类型的方式与其交互。
使用异步 action 来修改 store 中的状态
我们使用Redux Thunk 中间件来实现 Redux 的异步 action。
在哪里加入这个东西呢? 当我们创建 store 的时候加入中间件,即createStore的时候加入applyMiddleware。applyMiddleware是Redux 的一个 API,此 API 允许我们向 store 添加中间件
import { combineReducers, createStore, applyMiddleware } from 'redux';
import type { Store } from 'redux';
import thunk from 'redux-thunk';
import type { AppState } from './data.d';
import peopleReducer from './reducers/index';
const rootReducer = combineReducers<AppState>({
people: peopleReducer,
});
function configureStore(): Store<AppState> {
const store = createStore(rootReducer, undefined, applyMiddleware(thunk));
return store;
}
const storeData = configureStore();
export default storeData;
actions 和 action 创建函数
现在,我们把 action 创建函数修改为异步的形式,下面是添加用户名的异步 action
// src/redux/actions/index.ts
import type { Dispatch } from "redux";
function wait(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
const addPerson = (personName: string) => async (
dispatch: Dispatch<AddPersonAction>
) => {
await wait(200);
dispatch({
type: "AddPerson",
payload: personName,
} as const);
};
示例中,我们在用 wait延时函数伪造一个异步操作(如接口请求)。
addPerson函数返回一个函数,而不是像原来的同步函数那样返回 action 对象。- 内部函数接受一个参数
dispatch函数,作用是派发 action
注意,我们使用了 Dispatch<AddPersonAction>显式设置了 dispatch 参数的类型。 Dispatch 是 Redux 提供的一个泛型类型,然后我们将类型 AddPersonAction 传入了给Dispatch。
下面是 AddPersonAction 类型的定义
type AddPersonAction = {
readonly type: "AddPerson";
readonly payload: string;
};
接下来,我们改造删除用户的 action 创建函数
// src/redux/actions/index.ts
type RemovePersonAction = {
readonly type: "RemovePerson";
readonly payload: number;
};
const removePerson = (id: number) => async (
dispatch: Dispatch<RemovePersonAction>
) => {
await wait(200);
dispatch({
type: "RemovePerson",
payload: id,
} as const);
};
Reducer
Reducer 中action参数的类型与同步版本有些不同,如下
type Actions = AddPersonAction | RemovePersonAction;
我们在同步版本的代码中是通过 TS 的自动类型实现的 action 类型,但是这里我们需要创建 action 的类型。
reducer 函数的与之间同步版本的代码时一样的
function peopleReducer(state: Person[] = [], action: Actions) {
switch (action.type) {
case "AddPerson":
return state.concat({
id: state.length + 1,
name: action.payload,
});
case "RemovePerson":
return state.filter((person) => person.id !== action.payload);
default:
neverReached(action);
}
return state;
}
function neverReached(never: never) {}
链接组件
将 React 组件连接到 store。 useSelector用于从 store 中获取数据
const people: Person[] = useSelector((state: AppState) => state.people);
useDispatch用来派发 action
const dispatch = useDispatch();
...
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
dispatch(addPerson(newPerson));
...
};
...
const dispatchNewPerson = (id: number) => () => {
dispatch(removePerson(id));
};
...
<button onClick={dispatchNewPerson(person.id)}>Remove</button>
总结
Redux 需要像 Redux Thunk 这样的库来处理异步操作。
主要是通过异步 action 创建函数返回一个函数,该函数派发一个 action(注意: 在同步的 Redux 中的 action 创建函数返回的是一个对象)
但是后面有流行了一个叫 Redux saga的库用来处理 Redux 的异步操作,我们后面再简单讲一讲