当前位置: 首页 > 科技观察

使用Redux进行状态管理真的很简单!

时间:2023-03-14 16:23:26 科技观察

最近在一个项目中,想选择一个项目全局状态管理的工具。经过综合比较和考虑,最终选择了Redux。据说Redux很难上手。今天,通过一个案例,3个功能,助你快速掌握和实践制作!作为前端工程师,很多朋友可能对Redux的概念和使用还比较模糊。负担也比较重!但是通过研究,目前Redux的生态可以说是非常丰富的,这也更容易将其作为项目状态管理工具库引入。本文通过实际案例逆向解读Redux中的名词概念,同时使用@reduxjs/toolkit模块来简化Redux的使用。希望今天的分享可以帮助大家打开思路,拥抱Redux,提高工作效率,永不加班!,Redux基础在开头讲解概念名词,可能会增加大家入门的难度,所以这部分只给出对Redux最基本的理解。1.什么是Redux?Redux是一个JavaScript状态容器,提供可预测、可调试和集中的状态管理。2.可预测的特性:让您开发的应用程序具有稳定和可预测的行为,可以在不同的环境(客户端、服务器和本机程序)中运行,并且易于测试。集中管理:应用程序状态和逻辑的集中管理允许您开发强大的功能,例如撤消/重做、状态持久化等。可调试:ReduxDevTools让您可以轻松跟踪应用程序状态何时、何地以及如何发生变化。Redux的架构跟踪每一个变化,并且通过“时间旅行调试”,您甚至可以向服务器发送完整的错误报告。灵活:Redux可以与任何UI层框架一起使用,它小巧精简(只有2kB,包括依赖项),并且拥有庞大的插件生态系统来实现您的需求。3、设计思路既然Redux是一个状态管理库,那我们就掌握数据流的基本概念和原理。(1)单一数据源整个应用的全局状态存储在一个对象树中,而这个对象树只存在于唯一的Store(存储)中。单一的数据源让同构应用的开发变得更容易,在统一的对象树中维护和管理状态也会更容易!(2)单向数据流(one-waydataflow)Redux单向数据流使用状态来描述应用程序在特定时间点的状态。根据状态渲染视图。当发生某些事情时(例如用户单击按钮),状态会根据发生的事情进行更新,从而生成新的状态。根据新状态重新呈现视图。(3)不变性(Immutability)一般将状态(state)描述为一个大的JavaScript对象(ObjectTree),例如:conststate={isLoading:true,userInfo:{uid:1,wechat:'DYBOY2020',phone:177****7777,history:[1,2,3,4,5]}}由于JS的动态特性,对象是可以修改的。Redux想要记录每个状态。如果直接修改state中的Reference类型属性,势必会导致无法追踪和预测的状态变化。所以状态是只读的!改变状态的唯一方法是触发一个动作,它是一个描述已发生事件的普通对象。Redux期望所有状态更新都是不可变的。因此,每一次状态改变都不会修改原来的对象,而是修改前一个状态(state)的克隆对象,保证不变性和正确性。同时记录每次变化的状态。(4)纯函数更新状态纯函数:相同的输入,总是得到相同的输出,并且在执行过程中没有任何副作用的函数。为了保证数据变化的正确性,满足状态不变性的要求,引入纯函数作为更新状态的唯一途径。ReactHooks的状态管理融合了Redux的设计思想。毕竟Redux的作者DanAbramov直接被挖出来了!]以创建脚手架项目为例。借助@redux/toolkit,你不再需要关心如何组织和编写Reducer、Actioncreator、ActionType等,同时默认集成并支持异步Thunks。结合React16.x中的Hooks,使用useSelector(),useDispatch()在任何组件中消费Store。1.初始化项目首先使用create-react-app初始化一个TS+React环境项目。npxcreate-react-appcraapp--templatetypescript2,安装Redux相关依赖yarnaddreduxreact-redux@reduxjs/toolkitredux:核心状态管理库。react-redux:React框架的桥接层。@reduxjs/toolkit:一个让Redux更容易使用的助手。3.全局Store的创建所有的状态都放在Store中,所以需要一个统一的地方来管理。以一个计数器为例,./src/store下的文件结构如下:.├──index.ts//storeinstance,exportstateanddispatchtype└──reducers//收集所有reducer├──counter.ts//reducer,action,selectorforcounter└──index.ts//exportrootReducers,forIntegrateallreducers(1)store/index.tsimport{configureStore}from"@reduxjs/toolkit";importrootReducersfrom"./减速器”;//引入一个reducer集合//实例化store,全局唯一conststore=configureStore({reducer:rootReducers,});//导出Store中的状态(state)类型exporttypeRootState=ReturnType;//导出改变状态的Dispatch方法类型exporttypeAppDispatch=typeofstore.dispatch;//全局Provider消费默认导出storeexportdefaultstore;(2)store/reducers/index.tsimport{combineReducers}from'@reduxjs/toolkit'importcounterSlicefrom'./counter'//可以引入各种类型reducerconstrootReducers=combineReducers({counter:counterSlice//这里通过MAP的形式,自定义不同reducer的“命名空间”//...这里可以扩展添加任意reducer})//默认导出,消耗exp配置商店ortdefaultrootReducers(3)store/reducers/counter.ts接下来我们看看如何方便的创建一个Reducer。在使用Redux之前,总是需要手动创建多个文件,reducer、action、actioncreator,现在可以直接使用@redux/toolkit统一在一个文件中,结构化的描述Redux中的action和redcuer从“@reduxjs/toolkit”导入{createSlice,PayloadAction};从“..”导入{AppDispatch,RootState};//store/index.ts中声明的类型//使用createSlice创建reducer,actionconstCounterSlice=createSlice({name:"counter",//生成Action类型的前缀,例如:counter/incrementinitialState:{value:0,},reducers:{increment:(state)=>{state.value+=1;//这里默认通过immer处理,不会修改原来的state},decrement:(state)=>{state.value-=1;},incrementByAmount:(state,action:PayloadAction)=>{state.value+=action.payload;},decrementByAmount:(state,action:PayloadAction)=复制代码>{state.value-=action.payload;},},});//ActionCreator用于执行和返回描述如何更新状态Actionexportconst{increment,decrement,incrementByAmount,decrementByAmount}=CounterSlice.actions;//异步thunk,用于数据更新前异步处理数据exportconstincrementAsync=(amount:number)=>(dispatch:AppDispatch)=>{setTimeout(()=>{dispatch(incrementByAmount(amount));},1500);};//Selector,作为useSelector读取数据的函数参数exportconstcounterSelector=(state:RootState)=>state.counter.value;//Reducer,真正执行和修改状态的纯函数exportdefaultCounterSlice.reducer;以上写法可以作为“模板”使用,不用关心各种概念的组合,直接拿来用!console.log(CounterSlice)/*output:{name:'counter',actions:{increment,decrement,incrementByAmount,decrementByAmount},reducer}*/上面actions中的函数就是Action的创建者,比如return的increment()is:{type:'counter/increment'}executesincrementByAmount(5)andreturns:{type:'counter/incrementByAmount',payload:5}4.组件中要读写Stoe,首先是Store实例需要绑定到我们的应用程序在./src/index.tsx中添加以下内容:importReactfrom"react";importReactDOMfrom"react-dom";import{Provider}from"react-redux";//ImportProvider并将store绑定到applicationimportstorefrom"./store";//importstoreinstanceimportAppfrom"./App";import"./index.css";ReactDOM.render({/*Bindstore*/},document.getElementById("root"));结合react-redux提供的useSelector()和useDispatch()可以在我们自己定义的Counter组件中消费计数器状态(数据)。//文件位置:src/pages/counter/index.tsximportReactfrom"react";import{useDispatch,useSelector}from"react-redux";import{decrement,incrementAsync,counterSelector}from"@/store/reducers/counter";import"./index.scss";constCounterPage=()=>{constcount=useSelector(counterSelector)//读取计数值constdispatch=useDispatch()//获取调度,并使用操作更新状态return({/*sync-*/}dispatch(decrement())}>-

{`${count}`}
{/*async+*/}dispatch(incrementAsync(5))}>+
);};导出默认的CounterPage;实际效果:Timer效果演示纵观整个案例,相比不使用@redux/toolkit,明显提高了研发效率,减轻了研发的精神负担!三、拓展知识1、@redux/toolkitAPI在上述实际案例中使用如下API:configureStore():简化Store的创建,默认创建异步中间件,自动启用reduxdevtoolcombineReducers():简化合并reducer的操作,自动注入state和action。createSlice():简化和统一actioncreators和reducers的创建。以上三个API可以满足大部分场景。有了这个工具的帮助,大大减少了TypeScript类型定义的工作量。当然,如果你想了解更多@redux/toolkit便捷的API,推荐阅读官方文档:@redux/tookitAPIManual[2]。@redux/tookit的API手册-TypeScript类型相关[3]。2.Redux状态变化如果你对Redux的状态更新过程和原理感兴趣,这里强烈推荐阅读:Redux是如何实现状态变化来触发页面渲染的?[4].3、Redux的同步和异步数据流同步数据流:Redux的同步数据流图链接:https://umapu.cn/imgs/202203/8c767817cfd66ba6c45276c52e98c8b2.gif。异步数据流:Redux的异步数据流图链接:https://umapu.cn/imgs/202203/e7edf1f729772323b2aebaae824716eb.gif。4.总结强烈推荐选择Redux作为React项目的全局状态管理。结合React16.x的Hooks状态更新,非常方便,也符合函数组件的编码风格。我们来看看React的useContext和useReducer。它会工作吗?有没有感觉React和Redux是两兄弟?简单总结:在React项目中推荐使用Redux作为状态管理。需要掌握Redux中的设计思维。推荐使用@redux-toolkit,可以减轻精神负担,显着提升研发效率。掌握了@redux-toolkit之后,可以去阅读Redux的原始API,想想@redux-toolkit为什么要这样做?参考[1]create-react-app:https://create-react-app.dev。[2]@redux/tookitAPI使用指南:https://redux-toolkit.js.org/usage/usage-guide。[3]@redux/tookit的API使用手册-TypeScript类型相关:https://redux-toolkit.js.org/usage/usage-with-typescript。[4]Redux如何实现状态变化触发页面渲染?:https://juejin.cn/post/6945808822308962317。