阅读 166

「超详细React项目搭建教程八」使用 Redux-thunk实现Redux异步操作

上一篇文章中,我们在项目中集成了的同步 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的时候加入applyMiddlewareapplyMiddleware是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 的异步操作,我们后面再简单讲一讲

参考文档

www.carlrippon.com/managing-ap…

文章分类
前端
文章标签