1. 依赖安装 @reduxjs/toolkit react-redux typescript
npm install @reduxjs/toolkit react-redux typescript --save-dev
2. 创建公共仓库 store.ts
- 使用configureStore 注册 store
- 导出 state类型和dispatch的类型,方便后续代码引用
- counterSlice为单个reducer,注意 reducer是唯一的
import { configureStore } from '@reduxjs/toolkit';
import counterSlice from './slice/counter';
const store = configureStore({
reducer: {
counter: counterSlice, // 注册reducer
},
});
// 导出state 类型
export type TRootState = ReturnType<typeof store.getState>;
// 导出dispatch类型
export type TAppDisPatch = typeof store.dispatch;
export default store;
3. 根组件配置store
index.tsx
import "./index.css";
import React from "react";
import ReactDOM from "react-dom/client";
import { Provider } from "react-redux";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import store from "./store/store";
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
reportWebVitals();
4. createSlice ,创建单个reducer
- 创建slice
使用createSlice方法创建一个slice。每一个slice里面包含了reducer和actions,可以实现??榛姆庾啊?/p>
- 关键属性
- name
命名空间,可以自动的把每一个action进行独立,解决了action的type出现同名的文件。在使用的时候默认会把使用name/actionName。
- initialState
state数据的初始值。
- reducers
定义的action。由于内置了immutable插件,可以直接使用赋值的方式进行数据的改变,不需要每一次都返回一个新的state数据。
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { setCountAsync } from "./asyncThunk/setCountAsync";
type TSliceInitData = { counter: number };
// 初始化state
const initialState: TSliceInitData = {
counter: 0,
};
const counterSlice = createSlice({
name: "counter", // 唯一名字
initialState,
reducers: {
add: (state, action: PayloadAction<number>) => {
state.counter += action.payload;
},
sub: (state, action: PayloadAction<number>) => {
if (state.counter - action.payload < 0) {
return;
}
state.counter -= action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(setCountAsync.pending, (state) => {
// 等待状态
console.log("pending", state);
})
.addCase(setCountAsync.rejected, (state) => {
// 失败状态
console.log("pending", state);
})
.addCase(setCountAsync.fulfilled, (state, action) => {
// 成功状态
console.log("fulfilled", state);
console.log("fulfilled", action);
state.counter += action.payload;
});
},
});
// 注册到store中
export default counterSlice.reducer;
// dispatch 使用
export const { add, sub } = counterSlice.actions;
- 导出
1.counterSlice.actions 导出所有的修改函数方便页面使用
2.counterSlice.reducer 导出reducer在 store里面使用
- 具体reducer 函数的参数
1.参数1: 当前slice的state数据
2.参数2: 对象{type:"",payload:传参}
3.type:counter/getCount
5. 页面使用
- 关键hook介绍
1.useSelector():返回指定的state
const counter = useSelector(state=>state.rootCouter.counter);
2.useDispatch(): 修改函数const dispatch = useDispatch() dispatch(getCount())
- 优化hook,二次封装后统一处理后暴露新的hook
useReduxHook.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import { TAppDisPatch, TRootState } from "../store";
//此处对redux中的useDispatch, useSelector 进行二次封装方便项目中调用
export const useAppDispatch = () => useDispatch<TAppDisPatch>();
export const useAppSelector: TypedUseSelectorHook<TRootState> = useSelector;
- 页面使用
import React from "react";
import { useAppDispatch, useAppSelector } from "../../store/hooks/useReduxHook";
import { setCountAsync } from "../../store/slice";
import { add, sub } from "../../store/slice/counter";
import styles from "./index.module.css";
type TCounter = { name?: string; title?: string };
enum EButtonType {
ADD = "添加5",
DEL = "删除2",
}
const Counter: React.FC<TCounter> = () => {
const dispatch = useAppDispatch();
const { counter } = useAppSelector((state) => state.counter);
const buttonList = [
{
show: true,
element: (
<button
onClick={() => {
dispatch(add(5));
setCount((state) => state + 1);
}}
key={"add"}
>
{EButtonType.ADD}
</button>
),
},
{
show: true,
element: (
<button
onClick={() => {
dispatch(sub(2));
setCount((state) => state - 1);
}}
key={"del"}
>
{EButtonType.DEL}
</button>
),
},
];
return (
<>
<div>{buttonList.filter((item) => item.show).map((item) => item.element)}</div>
<h2 className={styles.counter}>当前:{counter}</h2>
</>
);
};
export { Counter };
-
页面截图
6. createAsyncThunk 的使用,主要用于异步请求
1.接受一个动作类型字符串和一个返回Promise函数,并生成一个pending/fulfilled/rejected基于该Promise分派动作类型的 thunk
2.参数1: slice的name/命名空间/函数名,参数2:执行函数
asyncThunk setCountAsync.ts
使用promise模拟接口请求
import { createAsyncThunk } from "@reduxjs/toolkit";
const setCount = (payload: number = 0): Promise<number> => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(payload + 10);
}, 1000);
});
};
const prefix = "counter/getCount";
export const setCountAsync = createAsyncThunk(prefix, async (payload?: number) => {
const res = await setCount(payload);
// 此处返回的结果,会在fulfilled中作为payload的值
return res;
});
7. extraReducer
异步函数配置
对应pending、rejected、fulfilled三种状态,
extraReducers: (builder) => {
builder
.addCase(setCountAsync.pending, (state) => {
// 等待状态
console.log("pending", state);
})
.addCase(setCountAsync.rejected, (state) => {
// 失败状态
console.log("pending", state);
})
.addCase(setCountAsync.fulfilled, (state, action) => {
// 成功状态
console.log("fulfilled", state);
console.log("fulfilled", action);
state.counter += action.payload;
});
},