1. 依賴安裝 @reduxjs/toolkit react-redux typescript
npm install @reduxjs/toolkit react-redux typescript --save-dev
2. 創(chuàng)建公共倉庫 store.ts
- 使用configureStore 注冊 store
- 導(dǎo)出 state類型和dispatch的類型,方便后續(xù)代碼引用
- counterSlice為單個reducer,注意 reducer是唯一的
import { configureStore } from '@reduxjs/toolkit';
import counterSlice from './slice/counter';
const store = configureStore({
reducer: {
counter: counterSlice, // 注冊reducer
},
});
// 導(dǎo)出state 類型
export type TRootState = ReturnType<typeof store.getState>;
// 導(dǎo)出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 ,創(chuàng)建單個reducer
- 創(chuàng)建slice
使用createSlice方法創(chuàng)建一個slice。每一個slice里面包含了reducer和actions携添,可以實現(xiàn)模塊化的封裝删窒。
- 關(guān)鍵屬性
- name
命名空間螟蝙,可以自動的把每一個action進(jìn)行獨立源武,解決了action的type出現(xiàn)同名的文件。在使用的時候默認(rèn)會把使用name/actionName舱污。
- initialState
state數(shù)據(jù)的初始值呀舔。
- reducers
定義的action。由于內(nèi)置了immutable插件扩灯,可以直接使用賦值的方式進(jìn)行數(shù)據(jù)的改變媚赖,不需要每一次都返回一個新的state數(shù)據(jù)。
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) => {
// 等待狀態(tài)
console.log("pending", state);
})
.addCase(setCountAsync.rejected, (state) => {
// 失敗狀態(tài)
console.log("pending", state);
})
.addCase(setCountAsync.fulfilled, (state, action) => {
// 成功狀態(tài)
console.log("fulfilled", state);
console.log("fulfilled", action);
state.counter += action.payload;
});
},
});
// 注冊到store中
export default counterSlice.reducer;
// dispatch 使用
export const { add, sub } = counterSlice.actions;
- 導(dǎo)出
1.counterSlice.actions 導(dǎo)出所有的修改函數(shù)方便頁面使用
2.counterSlice.reducer 導(dǎo)出reducer在 store里面使用
- 具體reducer 函數(shù)的參數(shù)
1.參數(shù)1: 當(dāng)前slice的state數(shù)據(jù)
2.參數(shù)2: 對象{type:"",payload:傳參}
3.type:counter/getCount
5. 頁面使用
- 關(guān)鍵hook介紹
1.useSelector():返回指定的state
const counter = useSelector(state=>state.rootCouter.counter);
2.useDispatch(): 修改函數(shù)const dispatch = useDispatch() dispatch(getCount())
- 優(yōu)化hook珠插,二次封裝后統(tǒng)一處理后暴露新的hook
useReduxHook.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import { TAppDisPatch, TRootState } from "../store";
//此處對redux中的useDispatch, useSelector 進(jìn)行二次封裝方便項目中調(diào)用
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}>當(dāng)前:{counter}</h2>
</>
);
};
export { Counter };
-
頁面截圖
image.png
6. createAsyncThunk 的使用惧磺,主要用于異步請求
1.接受一個動作類型字符串和一個返回Promise函數(shù),并生成一個pending/fulfilled/rejected基于該Promise分派動作類型的 thunk
2.參數(shù)1: slice的name/命名空間/函數(shù)名,參數(shù)2:執(zhí)行函數(shù)
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);
// 此處返回的結(jié)果丧失,會在fulfilled中作為payload的值
return res;
});
7. extraReducer
異步函數(shù)配置
對應(yīng)pending豺妓、rejected惜互、fulfilled三種狀態(tài)布讹,
extraReducers: (builder) => {
builder
.addCase(setCountAsync.pending, (state) => {
// 等待狀態(tài)
console.log("pending", state);
})
.addCase(setCountAsync.rejected, (state) => {
// 失敗狀態(tài)
console.log("pending", state);
})
.addCase(setCountAsync.fulfilled, (state, action) => {
// 成功狀態(tài)
console.log("fulfilled", state);
console.log("fulfilled", action);
state.counter += action.payload;
});
},