react+ts实战之 @reduxjs/toolkit

本文介绍Redux结合@reduxjs/toolkit的高效使用方式,包括简化工作流、异步处理及store持久化策略,对比传统Redux与现代工具包的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

Store的宗旨是为了复杂组件数据共享而设计,因此使用场景在设计层尽可能减少,否则极大加剧了项目的复杂度,正如官方所说的那样在你考虑用和不用的时候,那就是不用。

react的社区是足够大的,他不像vue那样cli官方给你构建一套完整的全家桶,如vue+vue-router+vuex,它更多的是像买车一样,给你提供选配的模式,出了本身的基础配置之外,你是要定制的皮制座椅(flux)还是星空顶(redux)…需要什么样的插件,自己就去市场选配什么插件,我们今天来讲一下主流的redux

一、redux的工作流

View调用store.dispatch发起Action->store接受Action(action传入reducer函数,reducer函数返回一个新的state)->通知store.subscribe订阅的重新渲染函数
在这里插入图片描述

二、redux曾经的使用案例

在前几年我们使用的就是这样,没错,非常完整规范的redux操作流程。reduceractionstore清晰明了,就是有一点,em…老太婆裹脚—又臭又长。所以在vue2发布以后,我们团队果断选择了vue直到现在,我个人目前也更为擅长vue的各个版本。
store.js

import {createStore} from 'redux'
import Reducer from './reducer'
const Store = createStore(Reducer)

export default Store; 

Reducer.js

const Reducer = (state,action)=>{
	if(typeof state === 'undefined'){
		return []
	}
	switch (action.type){
		case 'CON_TODO':
		return [...state,action.text]
		break;
		case 'TONN_DEL':
		state.splice(action.text,1)
		return state
		break;
		default:
		return state
			break;
	}
}


export default Reducer;

action.js

var Action = (text)=>{
	return{
		type:"CON_TODO",
		text:text
	}
}
export default Action; 

实际使用

import React from 'react'
import ReactDOM from 'react-dom'
import {createStore} from 'redux'
import Action from './Store/action.js'
import Store from './Store/store.js'
class App extends React.Component{
	constructor(props){
		super(props)
		this.shuju = this.shuju.bind(this)
		this.state={
			arr:Store.getState()
		}
	}
	del(a){
		Store.dispatch(Action1(a))
	}
	componentDidMount(){
		Store.subscribe(this.shuju)
	}
}
三、@reduxjs/toolkit 基于redux的工具库
3.1 基础案例

@reduxjs/toolkit这个是我们react实战项目中的一个重点知识,能帮我们解决redux多余的代码,而且让我们的代码更加清晰明了。以下是一个简单的案例,我们引入了一个关于counter的reducer切片

// store/index.ts
import { configureStore } from '@reduxjs/toolkit'
import counterSlice from './counterSlice'
export const store = configureStore({
  reducer: {
    counter: counterSlice
  },
})
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch

再来看一下具体的切片内容:

// store/counterSlice.ts	
import { createSlice, PayloadAction, createAsyncThunk } from '@reduxjs/toolkit'
export interface CounterState {
  isLogin: boolean
  value: number
  featchVal: number
}

const initialState: CounterState = {
  isLogin: false,
  value: 0,
  featchVal: 0
}

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    },
    incrementByAmount: (state, action: PayloadAction<number>) => {
      state.value += action.payload
    }
  }
})

// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions

export default counterSlice.reducer
3.2 @reduxjs/toolkit+store持久化

我们如果想加上store的持久化呢,也就是说对store的内容进行本地存储,这里使用的是redux-persist,此时注意了,@reduxjs/toolkit仍然支持,也就是说项目基本不需要引入redux的任何依赖,它本身就是对redux的功能的封装与补充,而且支持自定义的中间件middleware,再来看一下:

// store/index.ts
import { configureStore, combineReducers } from '@reduxjs/toolkit'
import { persistStore,persistReducer} from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import counterReducer from './counterSlice'
// ...

export const rootReducer = combineReducers({
  counter: counterReducer
})
const persistConfig = {
  key: 'root',
  storage ,
  blacklist:[]
}
const myPersistReducer = persistReducer(persistConfig, rootReducer)
export const store = configureStore({
  reducer: myPersistReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: false,
    })
})

export const persistor = persistStore(store)
3.3 处理异步

通常情况下store是没有处理异步的能力的,所以我们更多的是通过市场上提供的一些处理异步的解决方案,如redux-thunk
但是我们这里不需要,因为仍然有处理异步的方案,我们接着在counterSlice.ts中进行拓展

import { createSlice, PayloadAction, createAsyncThunk } from '@reduxjs/toolkit'

export interface CounterState {
  isLogin: boolean
  value: number
  featchVal: number
}

const initialState: CounterState = {
  isLogin: false,
  value: 0,
  featchVal: 0
}

export const fetchSettimeout = createAsyncThunk(
  'counter/fetchSettimeout', (id: number)=>{
    return new Promise<number>((resolve) => {
      setTimeout(()=>{
       resolve(id)
      },3000)
    })
  }
)

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    },
    incrementByAmount: (state, action: PayloadAction<number>) => {
      state.value += action.payload
    }
  },
  extraReducers: (builder) => {
    builder.addCase(fetchSettimeout.fulfilled, (state, action) => {
      console.log(state, action)
      state.featchVal = action.payload
    })
  }
})
// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer

我们用 fetchSettimeout方法封装了一个setTimeout来模拟了异步,这里只是一种写法,更多的查看点击前往官网

3.4 Hook封装

项目中我们希望借助react-redux来实现store的管理,但是不能直接去使用其中的一些api,可能会导致ts错误,因此我们单独拆出来做了一层Hook封装并进行类型声明

// src/hooks
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import { store } from '@/store'


export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
3.5 实际使用案例
root.render(
  <React.StrictMode>
    <Provider store={ store }>
      <App />
     </Provider>
   </React.StrictMode>
)
import { RootState,useAppDispatch,useAppSelector } from '@/hooks/store'
import { increment, decrement, fetchSettimeout } from '@/store/counterSlice'
const Home: React.FC = () => {
  const dispatch = useAppDispatch()
  const {featchVal, value} = useAppSelector((state: RootState) => state.counter)
  return (
    <div className="home">
       数据变化:{featchVal}
       数据:{value}
      <Button type="primary" onClick={() => dispatch(increment())}>store点击++</Button>
      <Button type="primary" onClick={() => dispatch(fetchSettimeout(21313213))}>点击请求</Button>
    </div>
}
export default Home
总结与对比

不知道大家看出来什么没有,有没有跟vuex的语法特别接近了呢,正所谓所谓合久必分,分久必合。任何一个框架除了性能作为首选推荐之外,其次就要考虑哪种语法更贴合开发的使用习惯,更简单明了。不知道当前的你是不是还在使用最原始的redux+local来管理数据呢???

vuex@reduxjs/toolkit
namename
stateinitialState
mutationscreateSlice-reducers
actionscreateAsyncThunk+extraReducers
modulesconfigureStore-reducer
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

槿畔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值