react官网链接 : zh-hans.react.dev/ ;
一:Redux的基本使用
1.为什么需要redux
JavaScript开发的应用程序,已经变得越来越复杂了,JavaScript需要 管理的状态越来越多,这些状态包括 服务器返回的数据、缓存数据、用户操作产生的数据等等,管理不断变化的state是非常困难的,状态之间相互会存在依赖,一个状态的变化会引起另一个状态的变化,View页面也有可能会引起状态的变化。当应用程序复杂时, state在什么时候,因为什么原因而发生了变化,发生了怎么样的变化,会变得非常 难以控制和追踪 。React是在视图层帮助我们解决了DOM的渲染过程,但是State依然是留给我们自己来管理,无论是组件定义自己的state,还是组件之间的通信通过props进行传递;也包括 通过Context进行数据之间的共享 。React主要负责帮助我们 管理视图,state如何维护最终 还是我们自己来决定,Redux就是一个帮助我们管理State的容器:Redux是 JavaScript的状态容器,提供了可预测的状态管理 。Redux除了和React一起使用之外,它也可以和其他界面库一起来使用(比如Vue),并且它非常小(包括依赖在内,只有2kb)
2.redux的核心理念
2.1 state
简单来说,就是交给redux管理的数据
2.2 action
Redux要求我们通过action来更新数据:
所有数据的变化,必须通过 派发(dispatch)action来更新
action是一个普通的 JavaScript 对象,用来描述这次 更新的type和content
强制使用action的好处是可以 清晰的知道数据到底发生了什么样的变化,所有的数据变化都是可跟踪,可预测的
下面就是几个简单的action示例:
2.3 reducer
但是如何将state和action联系在一起呢?答案就是reducer
reducer是 一个纯函数,reducer做的事情就是 将传入的state和action结合起来生成一个新的state
前面有提到,redux并不是只能在react中使用,接下来先演示一下redux的基本使用
第一步:定义一个reducer
第二步:定义一个store,传入reducer
第三步:在需要的地方使用
以上就是redux的基本使用,那将redux与react结合起来,该怎么使用呢?
二:React结合Redux
1.react-redux
redux官方帮助我们提供了 react-redux 的库,可以直接在项目中使用,并且实现的逻辑会更加的严谨和高效,具体使用步骤
第一步:定义设置redux
目录结构
index.js : reducer
constants.js :该文件会定义一些常量,这样涉及到字符串的地方可以使用常量替代,方便后期修改
actionCreators.js : 顾明思义,该文件会有一些创造action的函数,方便导出给外部组件使用
reducer.js : 定义初始数据并导出redeucer
第二步:在最外层注入Provider
第三步: 在组件中使用connect高阶函数将state和actions注入到组件的props中,通过props可以方便访问
2.ReduxToolkit结合redux Hooks 使用
Redux Toolkit 是官方推荐的编写 Redux 逻辑的方法。在前面我们学习Redux的时候应该已经发现,redux的编写逻辑过于的繁琐和麻烦。并且代码通常分拆在多个文件中(虽然也可以放到一个文件管理,但是代码量过多,不利于管理);Redux Toolkit包旨在成为编写Redux逻辑的标准方式,从而解决上面提到的问题;在很多地方为了称呼方便,也将之称为“RTK”; Redux Toolkit的核心API主要是如下几个:
configureStore:包装createStore以提供简化的配置选项和良好的默认值。它可以自动组合你的 slice reducer,添加你提供的任何 Redux 中间件,redux-thunk默认包含,并启用 Redux DevTools Extension。
createSlice:接受reducer函数的对象、切片名称和初始状态值,并自动生成切片reducer,并带有相应的actions。
createAsyncThunk: 接受一个动作类型字符串和一个返回承诺的函数,并生成一个pending/fulfilled/rejected基于该承诺分 派动作类型的 thunk。
2.1 基本结构
不难看出,结构相比较之前四个文件简单了许多,就是单独一个文件,不仅如此,这样还会帮我们省去action中type这个参数的编写,因为使用这种方式创建,reducers里面的函数名就会默认为type。
接下来,我们使用hooks写法代替connect注入props的写法
主要使用到的hooks有两个
2.1.1.useSelector
useSelector的作用是将state映射到组件中:
参数一: 要求传入一个回调函数, 会将state传递到该回调函数中; 回调函数的返回值要求是一个对象, 在对象编写要使用的数据, 我们可以直接对这个返回的对象进行解构, 拿到我们要使用state中的数据
参数二: 可以进行比较来决定是否组件重新渲染;
2.1.2 useDispatch
useDispatch非常简单,就是调用useDispatch这个Hook, 就可以直接获取到dispatch函数,之后在组件中直接使用即可;
不难发现,这样的写法相比较之后代码量和可读性都增强了很多,个人更喜欢这样的结合写法。
三:Redux的异步操作
1.react-redux 中使用异步
在redux中如何可以进行异步的操作呢?答案就是使用中间件 (Middleware)。学习过Express或Koa框架对中间件的概念一定不陌生 ,在这类框架中,Middleware可以帮助我们 在请求和响应之间嵌入一些操作的代码,比如cookie解析、日志记录、文件压缩等操作。redux也引入了中间件(Middleware)的概念: 这个中间件的目的是 在dispatch的action和最终达到的reducer之间,扩展一些自己的代码,比如 日志记录、调用异步接口、添加代码调试功能等等 ,我们现在要做的事情就是发送异步的网络请求,所以我们可以添加对应的中间件: 这里官网推荐的、包括演示的网络请求的中间件是使用 redux-thunk。redux-thunk是如何做到让我们可以发送异步的请求呢,我们知道,默认情况下的dispatch(action),action需要是一个 JavaScript 的对象 ,redux-thunk可以让 dispatch(action函数),action 可以是一个函数 ,该函数会被调用,并且会传给 这个函数一个dispatch函数和getState函数 ,dispatch函数用于我们之后再次派发action ,getState函数考虑到我们之后的一些操作需要依赖原来的状态,用于让我们可以获取之前的一些状态,从直接返回一个对象变成返回函数,自己可以在派发dispatch之前做更多额外的操作。
2.reduxTookit中使用异步
在之前的开发中,我们通过redux-thunk中间件让dispatch中可以进行异步操作。Redux Toolkit默认已经给我们继承了Thunk相关的功能:createAsyncThunk。
当createAsyncThunk创建出来的action被dispatch时,会存在三种状态:
pending:action被发出,但是还没有最终的结果;
fulfilled:获取到最终的结果(有返回值的结果);
rejected:执行过程中有错误或者抛出了异常;
我们可以在createSlice的entraReducer中监听这些结果:
并且在这里可以发现,我们可以直接操作state,这是因为reduxTookit内部帮我们封装了一层,便于我们使用。
四:Redux的模块拆分
如果将所有的状态都放到一个reducer中进行管理,随着项目的日趋庞大,必然会造成代码臃肿、难以维护, 那么我们可以怎样进行模块拆分呢,首先是将各种负责的reducer抽离出来。
4.1目录结构(使用react-redux)
4.2目录结构(使用 reactTookit)
然后使用提供的函进行reducer合并
4.1.1 combineReducers函数(react-redux)
那么combineReducers是如何实现的呢? 它也是 将我们传入的reducers合并到一个对象中,最终 返回一个combination的函数(相当于我们之前的reducer函数了),在执行combination函数的过程中,它会 通过判断前后返回的数据是否相同来决定返回之前的state还是新的state,新的state会触发订阅者发生对应的刷新,而 旧的state可以有效的阻止订阅者发生刷新;
4.2.1 configureStore函数(reactTookit)
configureStore:包装createStore以提供简化的配置选项和良好的默认值。它可以自动组合你的 slice reducer,添加你提供
的任何 Redux 中间件,redux-thunk默认包含,并启用 Redux DevTools Extension。