课程:React18 系统精讲
章节:Redux-toolkit
讲师:阿莱克斯刘
课程内容
【概念理解】什么是redux-toolkit
关于redux-toolkit,我们在本章开始的时候做了一点点基本的介绍,本节课,我们来深入剖析一下。
在深入研究源码之前,请同学们先打开redux-toolkit的官网 https://redux-toolkit.js.org/
,我们先来简单了解一下什么是redux-toolkit,接下来的课程我都会使用redux-toolkit的简称RTK。
首先,我们看到在主页的副标题红,明确的写着这是official的、opinionated的、batteries的、以及efficient的工具。
-
Official 的意思就是说他是redux团队官方推出的插件,官办背景的背书质量以及后续的维护肯定是有保障的。
-
第二个 Opinionated 的意思就是,他不仅是一个工具,而且还会提供一个更加简单的架构思想。比如, 合并action与reducer,再比如自动处理异步逻辑等等。
-
第三个batteries,意思是会他会自带一系列官方推荐的工具集,方便我们的项目集成。
- 最后,efficient,就是高效的意思。关于这一点,我先放一点代码的对比图片,在完成本章后,同学们明显的看到使用RTK与传统redux的代码效率的区别。
学习前提
再次明确一下我们本章的学习前提,学习redux-toolkit,我们必须掌握redux以及react-redux的基本原理,还对这两个概念不理解的同学建议先回到前面几张复习一下。
源码解读
接下来,我将会带着同学们解读源码,我现在打开的是RTK的代码库。研究源码,第一件事情就是要先研究一下他的依赖项目。请打开package.json.
package.json.
在depanency这个属性下面,我们可以看到RTK需要4个依赖。
-
redux:我们从最简单的开始,当然,RTK需要安装redux才能工作,什么意思呢?就是说如果我们的项目安装了RTK,就不需要重复安装redux了。但是,如果同学们观察的足够仔细,就会发现他的依赖项并不包含react-redux。所以说,redux-toolkit并不只是给react项目使用的,你要是愿意,也可以在vue、或者angular、甚至是jQuery项目中使用。也就是说,如果你想再redux-toolkit项目中使用react-redux,那么你也得手动安装
-
第二个依赖,RTK自带了redux-thunk来处理异步逻辑,thunk在RTK项目中是默认启动的,请注意,我这里说的是默认启动,也就是说你自己在开发过程中也可以关闭或者是使用redux-sage等其他异步处理中间件来替代thunk。
-
第三个reselect,这也是一个比较流行的redux插件,他可以帮助我们在使用state selector的时候记住当前的状态,这样就可以防止你的组件在不需要的时候被无意识的渲染了。这个插件我还是比较喜欢的,平时工作中也会使用,不过我们的课程属于react入门,所以就暂时不深入讲解了。有机会我也做一个补充材料,给同学们阅读。
-
最后一个,也是最难理解的,就是immer。这个immer是一个非常有意思的插件,他允许我们把state从immutabe转化为mutable,也就是说,我们可以在reducer中直接更改state数据了。第一次听到这个思路的时候,理性告诉我这是不对的,因为这样就等于在违反reducer函数式编程的理念。而在redux的官方文档中,
Immutable Update Patterns
明确说明reudx的一个核心原则就是Immutable ,我们不能直接更新store,而是要通过数据替换来解决更新问题。
但是,在亲身体验过immer以后,我觉得“真香”。。而且,如果同学们继续往下看文档,我们又会看到一个Immutable Update Utility Libraries
这个自然段, 这里又是另一番描述,告诉我们官方支持使用immer来直接修改state,所以对于这种前后矛盾的说法,我也不知道该做什么评论。
那么,这个问题就留给同学们自己思考,不管怎么样、不管文档说什么,我们都一定要保持自己的独立思考习惯,我们可以继续使用immutable状态state,也可以使用immer来直接更新state,这完全取决于自己对代码、对项目、对架构的理解。
而我也将会在接下来的课程中介绍使用immer和不使用immer的两种方式,请同学们自己来做判断。
Api 详解
现在,请同学们回到RTK的官方文档,请同学们点击右上角的API按钮,我们先来学习一下他的api文档,在左边的api导航栏中,我们可以看到这里列出了RTK所有的api,这里总共有10个主要的api加上一些其他不重要的api,我们将会在接下来的代码实战中逐个研究。不过,有一点值得高兴的是,RTK所有的文档都是typescript写的,这与我们的项目契合度非常高。
在所有的api中,有两个api特别值得我们关注,不是createReducer、不是createAction,而是createSlice和configStore。
createReducer
不过,重要的事项留在后面,我们先来看看createReducer。文档的第一个例子很眼熟,这就是普通redux架构中的reducer。reducer很简单,就是couterReducer函数接受两个参数,state和action,然后返回新的state。
而对于RTK项目来说,我们有Reducer有两种创建方法,第一种是"Builder Callback" Notation
,使用回调构建对象。
另一种是"Map Object" Notation
,使用映射对象。我更建议同学们在开发过程中使用第二种方法,映射对象
的方法,因为我认为这种书写方式更直观,更容易理解。
所以,我们couterReducer的创建方式非常简单,如下图所以,直接使用createReducer函数,第一个参数是initialState,也就是初始化state,而第二个参数就是action与reducer的混合体。
在使用createReducer以后,我们不需要额外定义action类型了,reducer中也可以免去switch语句。当reducer和action合并以后,没有了模版代码,我们的代码量至少能够减少30%以上。
createAction
而如果我们非要写清楚action,那么你也可以使用createAction函数,比如这样
通过这种方式,我们就可以向外以对象的形式来输出action了。我们也可以稍微看一眼文档。文档很简单,几乎不用解释了吧,就是把我们之前写的各种 action creator封装起来,加个范型定义一下payload类型而已。
createSlice
不过,在99%的情况下,我们都不会直接使用createAction和createReducer这两个函数,取而代之的是使用createSlice。
createSlice绝对是RTK核心中的核心,我们先来看看文档是怎么说的。
什么意思呢?createSlice函数接受初始化state和对象化映射后的reducer,可以将store以切片slice的方式分割成为不同的部分,每个部分都会独立、而且自动生成相对应的action与state对象。
我们直接来看一下代码示例:
-
createSlice函数的参数是个对象,
-
第一个字段是slice名称,相当于分割store以后的命名空间。
-
第二个字段是initialState,就是数据初始化。因为initialState是redux数据启动的必要前提,而在传统的reducer中,我们必须要在参数上使用等号来定义initialState,有时候程序员会忘记写上initialState,这时候程序就会报错。现在,在RTK中我们直接使用在对象中强制定义了initialState,这样就可以完美避免state数据悬空的问题。
- 第三个字段是reducer,但是这是一个特殊的reducer,因为他把action和reducer结合在一起了,这与我们刚刚介绍过的createReducer是一样的。
不过,不知道同学们有没有发现一个细节,就是在reducer的case中,我们可以看到state实际上是被直接修改了的。这是怎么回事?为什么可以直接修改state,而不是重建state?直接修改state是否违反了immutable的原则呢?
其实,这就是刚刚给同学们介绍的immer的神奇所在了,因为RTK自动加载了immer,所以当我们在直接修改state的时候,immer实际上是在最底层帮我们重建state对象的。所以,从原理上来说这样的修改是有效的,而且并不违背immutable的原则。只不过是我自己对这种可变更state的思路有一点抵触情绪而已。关于state应该是immutable还是mutable这个问题,欢迎同学们加入课程的讨论群,我们一起一边讨论一边学习。
不过,好像这样看起来,这个createSlice函数也没什么了不起的,不就是把reducer和action捆绑在一起了吗?
好的,请同学们把页面往下拉,找到example,我们来仔细研究一下。大体上来说,这个案例与刚刚我们讲解的差不多,但是有两个地方值得我们注意。
- 第一个,是couter slice中的multiply乘法reducer,它也包含两个字段,第一个是reducer本身,就是multiply乘法的处理函数,
只不过多了一个“extraReducers”字段,extraReducers是用来处理js模块环形引用问题的,我们先暂时跳过。
案例中有两个slice,一个conter,一个user。我们还是会使用combineReducers函数把两个reducer合并起来,不过,请注意,reducer是从slice中来的。然后使用createStore来创建store。
然后,神奇的事情就发生了,当使用dispatch的时候,我们可以通过对象的形式,直接访问action,而action是有reducer自动生成的,他所对应的是不同的case的action creator。这样的好处是什么呢?好处就是,我们把action干掉了,代码看起来干净整齐了很多,同时,我们也不需要再使用switch语句来判断action类型了,不仅杜绝模版简化了代码,而且还从源头上解决了string类型action出错的可能性,
值得注意的是倒数第四行代码,console log打印了couter的action对象,结果我们看到的输出是一个字符串,这个字符串是RTK自动生成的,前半段是slice名称,就相当于是命名空间,而后半对所对应的就是reducer的对象的名称,也是action类型的名称。
当然,以上这一切都只是使用了redux-toolkit以后的语法糖而已。redux-toolkit与react-redux一样,使用特定的语法结构来在更高的层次上对redux的架构进行了封装。但落实到原理级别,还是离不开action、reducer、state、middleware这些基本组件。所以,想熟练使用redux-toolkit,能够掌握redux的基本原理是绝对的前提条件。
configureStore
最后,我们来看一下configureStore。ok,一开篇就开始自卖自夸了,介绍说configStore是一个非常友好的创建redux store的方式,也就是说,我们可以通过configureStore来代替redux原生的createStore函数了。
configStore会带有一些预先定义好的参数,而参数也是以对象的形式传递进来的。
-
reducer:就不用多说了吧,定义了store的state数据的初始化与变化过程
-
Middleware:中间件,Middleware,值得说道一下。这里会默认传入两个中间件,一个是react-thunk,另一个就是immer,我们可以通过getDefaultMiddleware函数取得默认中间件。
-
devTools:开启以后,我们每次dispatch action,都会在console中打印action和state的数据,我们会在接下来代码中尝试一下这个选项。
-
preloadedState: preloadedState,enhancers 与原生reudx 的createstore用法一样,preloadedState用来设置初始化state,而且可以覆盖reducer中的initialState
-
Enhancers:就是提供另一个途径来添加中间件。最后这两个字段用处比较少,可以不需要太关注。
共同学习,写下你的评论
评论加载中...
作者其他优质文章