首先,React Toolkit是React新提出的类Redux状态管理模式。该技术的提出是为了解决Redux的三个常见问题:
- 🤨”配置Redux储存太复杂了“
- 🤔”必须添加很多包才能让Redux做任何有用的事情“
- 😯”Redux需要太多样板代码“
更多介绍可以看官方文档
安装
官方提供了基于 React+Js 或者 React+Ts 的模块(脚手架):
1 2 3 4 5
| # Redux + Plain JS template npx create-react-app my-app --template redux
# Redux + TypeScript template npx create-react-app my-app --template redux-typescript
|
如果你想在已有的项目上安装,使用如下命令:
1 2 3 4 5
| # NPM npm install @reduxjs/toolkit or # Yarn yarn add @reduxjs/toolkit
|
当然有你需要先有react-redux
使用
官方的例子是一个加减demo,我这里是一个购物车的案例
安装
1
| npm install @reduxjs/toolkit react-redux
|
一个项目仅有一个state,和redux一样,也是需要将各个分开的状态统一整合管理
注意:counterSlice
和shopCarList
是用户自定义的slices
,在redux中称之为reducer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { configureStore } from "@reduxjs/toolkit";
import counterSlice from './slices/slices_shoplist'; import shopCarList from "./slices/slices_shopCar";
const store = configureStore({ reducer:{ counter:counterSlice, shopCar:shopCarList } })
export type RooState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export default store
|
在index.ts
文件中引入<Provider>
组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import ReactDOM from 'react-dom/client'; import App from './App';
import { Provider } from 'react-redux'; import store from './redux/store';
const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement );
root.render( <Provider store={store}> <App /> </Provider> );
|
创建各个状态文件在redux/slices/slices_shoplist
ts类型注解不做过多解释,PayloadAction
是官方提供的TS接口,<>中注解使用该reducer传入参数的类型
每个reducer方法都有形参state,action
,state
指向状态,action
中的action.payload
代表调用该reducer方法的时候传入的参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| import { createSlice,createAsyncThunk,PayloadAction } from "@reduxjs/toolkit";
export interface initArr { key:number, title:string, price:number, quantity:number }
interface initType { shopList:Array<initArr> }
const initial:Array<initArr> = [ { key:1, title:'测试文本01', price:100, quantity:100, }, { key:2, title:'测试文本02', price:200, quantity:100, }, { key:3, title:'测试文本03', price:300, quantity:100, }, ];
const initialState:initType = { shopList:initial }
const counterSlice = createSlice({ name:'counter', initialState, reducers:{ addShop:(state,action:PayloadAction<initArr>)=>{ state.shopList.push(action.payload) }, deleShop:(state,action:PayloadAction<number>)=>{ state.shopList = state.shopList.filter((tit:initArr)=>{ return tit.key !== action.payload }) } }, })
export const { addShop, deleShop } = counterSlice.actions;
export default counterSlice.reducer;
|
在组件中使用,在函数组件中使用钩子useSelector,useDispatch
获取初始状态以及dispatch,代码过多请仅关注标注部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| import React, { useRef } from 'react' import { useSelector,useDispatch} from 'react-redux' import type { RooState } from '../../redux/store';
import styled from 'styled-components'; import { addShop } from '../../redux/slices/slices_shoplist';
export const OneDiv = styled.div` h1{ font-size:25px; font-weight:600; color:#5592fa; } ` const Input = styled.input` border:1px solid #5592fa; border-radius:5px; width: ${(props)=>props.width}; height: 30px; margin-top:20px; font-size:18px; padding-left:10px; outline:none; ` const Span = styled.span` color:#5592fa; padding: 0px 9px; ` const Button = styled.button` width:100px; height: 30px; background-color:#5592fa; color:#fff; padding: 0px 9px; border:none; cursor: pointer; margin:30px 0px 0px 80px; `
const One = () => { const shopTitle = useRef<HTMLInputElement>(null); const shopPrice = useRef<HTMLInputElement>(null); const shopQuantity = useRef<HTMLInputElement>(null); const ULS = useSelector((state:RooState)=>state.counter); const dispatch = useDispatch();
const addShopList = ()=>{ if (shopTitle.current?.value.trim()&&shopPrice.current?.value.trim()&&shopQuantity.current?.value.trim()) { let obj = { key:+new Date(), title:shopTitle.current?.value, price:parseInt(shopPrice.current?.value), quantity:parseInt(shopQuantity.current?.value) } dispatch(addShop(obj)) shopTitle.current.value = ''; shopPrice.current.value = ''; shopQuantity.current.value = ''; }else{ alert('请输入内容'); } }
return ( <OneDiv> <h1>添加商品</h1> <ul> <li> <Span>商品名称:</Span> <Input type="text" width="400px" ref={shopTitle} placeholder="名称"></Input> </li> <li> <Span>商品价格:</Span> <Input type="text" width="50px" ref={shopPrice} placeholder="价格"></Input> </li> <li> <Span>商品数量:</Span> <Input type="text" width="50px" ref={shopQuantity} placeholder="数量"></Input> </li> <li> <Button onClick={()=>addShopList()}>添加商品</Button> </li> </ul> </OneDiv> ) }
export default One
|
关于异步
官方提供的Demo中的异步属于简化写法,并不会对异步进行监听
1 2 3 4 5 6
| export const incrementAsync = (amount) => (dispatch) => { setTimeout(() => { dispatch(incrementByAmount(amount)) }, 1000) }
|
第二种写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| import { createSlice,createAsyncThunk } from "@reduxjs/toolkit";
export var loadPic = createAsyncThunk('weather/loadPic', async () => { return new Promise((resolve:(str:string)=>void,reject)=>{ setTimeout(()=>{ resolve('数据'); },1000) }) });
var counterSlice = createSlice({ name:'counter', initialState:{ value:0 }, reducers:{ incremented:(state)=>{ state.value+=1 }, decremented:(state)=>{ state.value-=1 }, add:(state,action)=>{ state.value+=action.payload; } }, extraReducers(builder){ builder .addCase(loadPic.pending,(state)=>{ console.log(state); }) .addCase(loadPic.fulfilled,(state)=>{ console.log(state); }) } })
export const {incremented,decremented,add} = counterSlice.actions;
export default counterSlice.reducer;
|