Redux 是 React 应用状态管理的一个比较流行的库!
在本篇文章中,我们来交流下
- 如何使用 Typescript 以强类型的方式管理 Redux 状态
- 如何使用 React-Redux 的 Hooks 函数让 React 组件和 Redux 状态进行交互
安装依赖
yarn add redux react-redux
yarn add @types/react-redux --dev
# 安装 redux开发者工具
$ yarn add redux-devtools --dev
话说实践出真知,下面我们通过 Redux 实现一个用户名的添加和删除。以便加深我们对 Redux 的理解
State
我们将以下类型作为 stores 的状态类型
// src/redux/data.d.ts
// 用户的字段类型
type Person = {
id: number;
name: string;
};
// 所有用户的类型
type AppState = {
people: Person[];
};
可以发现,我们的应用状态是一个 people
,它是包含 id
/name
的数组类型
状态我们尽量简化,因为我们主要专注于 Redux Store 以及 React 组件如何以强类型的方式进行交互。
Actions 和 action 创建函数
众所周知,在 Redux 中改变状态必须发起一个 action,action 中必须包含
- 发起的动作是什么
- 发起的动作需要传递什么东西
在我们的例子中有两个 action
- AddPerson:当一个用户名被添加时触发,该动作中包含了新的用户的名称
- RemovePerson:当一个用户名被删除时触发,该动作中包含了用户的
id
接下来是 action 创建函数,它的作用是创建并返回 action 对象,下面的是我们为两个 action 实现的 action 创建函数
// src/redux/actions/index.ts
import actionTypes from "./actionTypes";
export const addPerson = (personName: string) => {
return {
type: actionTypes.ADD_PERSON,
payload: personName,
} as const;
};
export const removePerson = (id: string) => {
return {
type: actionTypes.REMOVE_PERSON,
payload: id,
} as const;
};
注意,我们没有给payload
明确类型,因为 action 创建函数可以自动推断是什么类型。
Reducer
reducer 是一个接收 state 参数和 action,并用于更新 state 的函数,
首先我们为 action 参数定义 Typescript 类型
type Actions = ReturnType<typeof addPerson> | ReturnType<typeof removePerson>;
这是所有 action 的联合类型。 我们使用 typeof
关键字获取 action 创建函数的类型,然后使用ReturnType
获取这些函数的返回类型。 使用这种方法,我们不需要显式为 action 对象创建类型。
reducer 函数如下所示
import { addPerson, removePerson } from "../actions/index";
import type { Person } from "../data.d";
import actionTypes from "../actions/actionTypes";
type Actions = ReturnType<typeof addPerson> | ReturnType<typeof removePerson>;
const initialState: Person[] = [{ id: "1", name: "小萝莉" }];
export default function peopleReducer(state = initialState, action: Actions) {
switch (action.type) {
case actionTypes.ADD_PERSON:
return state.concat([
{
id: (Math.random() * 1000000).toFixed(0),
name: action.payload,
},
]);
case actionTypes.REMOVE_PERSON:
return state.filter((person) => person.id !== action.payload);
default:
break;
}
return state;
}
我们显式地设置了函数参数类型,且不指定返回值的类型(由 TS 的类型推断来判断)。
请注意, switch
语句中的 action.type
是强类型的,因此,如果我们在case
中输入错误的值,将引发报错。
Store
我们使用 Redux 中的 createStore
来创建一个生成 store 的函数
import { combineReducers, createStore } from "redux";
import { Store } from "redux";
import { AppState } from "./data.d";
import peopleReducer from "./reducers/index";
const rootReducer = combineReducers<AppState>({
people: peopleReducer,
});
function configureStore(): Store<AppState> {
const store = createStore(rootReducer, undefined);
return store;
}
const storeData = configureStore();
export default storeData;
使用 Redux 中的combinedReducers
函数创建rootReducer
store 的类型设置也很简单:使用 Redux 中的Store
泛型类型,在我们的示例中为AppState
:
链接组件
终于走到组件这一步了,<( ̄ ▽  ̄)/
首先我们需要使用 react-redux 中的 Provider
组件包裹顶层组件,然后将 store 传递到Provider
组件中:
const App = () => (
<Provider store={store}>
<Page />
</Provider>
);
在子组件内部,我们可以使用 React Redux 的useSelector
钩子从 store 中获取数据
// src/pages/home/homeByFunc.tsx
import React from "react";
import { useSelector } from 'react-redux'
const Index: React.FC = () => {
.......
const people: Person[] = useSelector((state: AppState) => state.people);
.......
}
useSelector
函数接收的参数也是一个函数,该函数从 store 获取状态并返回相关数据。 并且使用AppState
类型显式设置 state 参数的类型。
接下来,我们使用 React Redux 的useDispatch
钩子来调用 action:
// src/pages/home/homeByFunc.tsx
import React from "react";
import { useDispatch } from 'react-redux'
const Index: React.FC = () => {
.......
const dispatch = useDispatch();
.......
}
useDispatch
返回一个我们称为dispatch
的函数。 然后,我们通过将 action 创建函数传递到dispatch
中来触发 action:
// src/pages/home/homeByFunc.tsx
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>
难道这就结束了吗??♂️ 不,离搭建一个完整的 React 应用有些距离。那么在下一篇文章中,我会告诉你如何在项目中实现异步Redux
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!