1. 前言
- toolkit
- 最近創(chuàng)建 react腳手架項(xiàng)目的時(shí)候發(fā)現(xiàn), 默認(rèn)安裝的 react版本已經(jīng)變?yōu)?code>18X了,
- 原來的項(xiàng)目換成
18X
也能運(yùn)行,但是 redux中的createStore
出現(xiàn)了橫崗也就是被廢棄了- 那就看看 新出的啥東西代替了
createStore
,體驗(yàn)下好用不
2. 是什么 what
Redux Toolkit包旨在成為編寫Redux邏輯的標(biāo)準(zhǔn)方式。它最初的創(chuàng)建是為了幫助解決關(guān)于 Redux 的三個(gè)常見問題:
- 配置 Redux 存儲(chǔ)太復(fù)雜了
- 我必須添加很多包才能讓 Redux 做任何有用的事情
- Redux 需要太多樣板代碼
3. 環(huán)境安裝
注意不要拼錯(cuò) @reduxjs/toolkit
npm install @reduxjs/toolkit react-redux
yarn add @reduxjs/toolkit react-redux
react-redux 也需要單獨(dú)安裝
4. configureStore
configureStore
替代createStore
- 配置簡(jiǎn)單,設(shè)置默認(rèn)值也方便
- src/store/index.js
// 引入
import {configureStore} from '@reduxjs/toolkit'
import counterSlice from "../pages/basic/counterSlice"
import mySlice from "../pages/mySlice"
export default configureStore({
reducer:{
rootCounter:counterSlice,
rootMy:mySlice
}
})
- 這里 reduer直接合并成一個(gè)唯一的 根root了
- 原有的
combineReducers
這個(gè)合并函數(shù)就用不到了- 注意自己配置的 reducer的 key值 和 對(duì)應(yīng)的value值
- 我這里把單獨(dú)的
reducer
放到和頁(yè)面同級(jí)了,這個(gè)根據(jù)自己的習(xí)慣,放到store
下面新建目錄存放所有的reducer
也行
5. 根組件配置 store
- 入口index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import store from './store'
import {Provider} from 'react-redux'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
6. createSlice reducer編寫
1. 創(chuàng)建slice
使用createSlice方法創(chuàng)建一個(gè)slice晓褪。每一個(gè)slice里面包含了reducer和actions貌夕,可以實(shí)現(xiàn)模塊化的封裝。
所有的相關(guān)操作都獨(dú)立在一個(gè)文件中完成貌踏。
2. 關(guān)鍵屬性:
- name
命名空間,可以自動(dòng)的把每一個(gè)action進(jìn)行獨(dú)立,解決了action的type出現(xiàn)同名的文件蚀之。在使用的時(shí)候默認(rèn)會(huì)把使用name/actionName
- initialState
state數(shù)據(jù)的初始值
3.reducers
定義的action。由于內(nèi)置了immutable插件捷泞,可以直接使用賦值的方式進(jìn)行數(shù)據(jù)的改變足删,不需要每一次都返回一個(gè)新的state數(shù)據(jù)。
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
counter: 100,
user: {
name: "yzs",
job: "全棧",
},
};
export const counterSlice = createSlice({
name: "counterSpace", // 命名空間锁右,在調(diào)用action的時(shí)候會(huì)默認(rèn)的設(shè)置為action的前綴,保證唯一.不重名
initialState,
reducers: {
// reducer函數(shù) state當(dāng)前組件的數(shù)據(jù)
//第二個(gè)參數(shù)為{payload:{},type:"""} 想想就寫法或者vuex
increment(state) {
state.counter += 100;
},
decrement(state, actions) {
// actions == {payload:{},type:"""}
console.log("decrement---actions", actions);
state.counter -= actions.payload;
},
updateUser(state, { payload }) {
console.log("updateUser-------payload", payload);
// 引用類型 注意 賦值的寫法
state.user = {
...state.user,
...payload,
};
},
},
});
export const { increment, decrement, updateUser } = counterSlice.actions;
export const selectCount = (state) => state.rootCounter.counter;
export const selectUser = (state) => state.rootCounter.user;
export default counterSlice.reducer;
3. 導(dǎo)出
- counterSlice.actions 導(dǎo)出所有的修改函數(shù)方便
頁(yè)面
使用- counterSlice.reducer 導(dǎo)出reducer在
store
里面使用
4. 具體reducer 函數(shù)的參數(shù)
參數(shù)1: 當(dāng)前slice的state數(shù)據(jù)
參數(shù)2: 對(duì)象{type:"",payload:傳參}
type:counterSpace/decrement
type就是之前的 actions用switc/case
來匹配很麻煩,現(xiàn)在簡(jiǎn)潔了
type構(gòu)成 slice的 name命名空間/具體的修改函數(shù)
payload 要和傳的時(shí)候保持一致
7. 頁(yè)面使用
7.1. useSelector()
- 返回指定的 state
// 這樣寫太長(zhǎng)了 麻煩
const counter = useSelector(state=>state.rootCouter.counter);
- rootCouter這個(gè)
key
來源于 根store
里面配置的reducer- 這樣寫太長(zhǎng)了 麻煩
- 在這個(gè)
slice
里面我做了統(tǒng)一處理
export const selectCount = (state) => state.rootCounter.counter;
export const selectUser = (state) => state.rootCounter.user;
- 頁(yè)面使用
import { useSelector, useDispatch } from "react-redux";
import {
increment,
decrement,
updateUser,
selectCount,
selectUser,
} from "./normalSlice";
export default function Counter() {
const dispatch = useDispatch();
// let counter = useSelector(state=>state.rootCouter.counter)
const counter = useSelector(selectCount);
console.log("頁(yè)面---counter:", counter);
const user = useSelector(selectUser);
// 這樣可以直接解構(gòu)出當(dāng)前 slice所有的 state
// 具體用哪種 看自己心情
const { counter } = useSelector((state) => state.rootCouter);
return ( <div> 布局看下面 </div>)
7.2 useDispatch()
const dispatch = useDispatch(); 修改函數(shù)
return (
<div>
<h1>reduxjs/toolkit 基礎(chǔ)用法</h1>
<hr />
<button onClick={() => dispatch(increment())}>+</button>
<span>{counter}</span>
<button onClick={() => dispatch(decrement(666))}>-</button>
<hr />
<h1>姓名:{user.name} ---職業(yè):{user.job}</h1>
<button onClick={()=>{dispatch(updateUser({name:'Michael'}))}}>改名</button>
<button onClick={()=>{
dispatch(updateUser({job:'自由職業(yè)者'}))}
}>轉(zhuǎn)行</button>
</div>
);
}
- payload傳參和 reducer保持一致
- 引用類型的 修改注意
8. 異步 createAsyncThunk()
- 接受一個(gè)動(dòng)作類型字符串和一個(gè)返回
Promise
函數(shù)失受,并生成一個(gè)pending/fulfilled/rejected基于該Promise
分派動(dòng)作類型的 thunk- 用 fetch請(qǐng)求模擬一個(gè)異步
3.createAsyncThunk("counterSpace/getList",()=>{})- 參數(shù)1: slice的name/命名空間/函數(shù)名
// return 不要忘記
export const getList = ( ) => {
return fetch("https://.XX.cn/api/news").then(res=>res.json());
};
export const getListAsync = createAsyncThunk("counterSpace/getList",async()=>{
const res = await getList()
return res// 此處的返回結(jié)果會(huì)在 .fulfilled中作為payload的值
});
9.extraReducers
- 異步函數(shù)配置
createSlice({
name: "counterSpace",
initialState,
reducers:{},
extraReducers: (builder) => {
builder
.addCase(getListAsync.pending, (state) => {
console.log("pending",state);
})
.addCase(getListAsync.rejected, (state, err) => {
console.log("rejected 失敗",err);
})
.addCase(getListAsync.fulfilled, (state, action) => {
console.log("fulfilled 成功",state);
console.log("fulfilled action",action);
state.list = action.payload
});
},
});
- 這個(gè)配置基本就是套路
- 只需要把函數(shù)名字改為通過
createAsyncThunk()
創(chuàng)建的函數(shù)名- 根據(jù)自己的業(yè)務(wù)場(chǎng)景 寫賦值邏輯就行
10. 頁(yè)面使用異步函數(shù)
<button onClick={()=>dispatch(getListAsync('異步模擬'))}>異步</button>
<ul>
{
listData.map((news)=>{
return <li key={news.id}>{news.title}</li>
})
}
</ul>
- 效果圖
1.png- 也可以傳參數(shù)