Redux入门之实现一个迷你版的Redux

Redux是一种数据架构模式,它可以用来管理应用的状态。

之前一直在做Angular的项目,没有使用到过Redux,对于Redux的使用场景和原理都不是很清楚,看资料时作者自己实现了一个Redux,在这里记录一下,加深对Redux原理的理解。

一、基本原理

首先,我们要明白的是:

  • 状态的改变一定是有原因的,这个原因的表现叫作action。
  • 有了action之后,状态不可能自己就改变,需要我们去操作它,这个操作就是reducer函数。
  • 操作状态需要2个条件,之前的状态和触发操作的action,这个操作是为了改变状态,使以这个操作返回一个新的状态。

按照上面的说明,我们先定义一下action和reducer的接口。

 1 // 首先,状态改变一定是由action引起的,至于这个action是由什么触发的(用户点击button、异步通信)我们不关心
 2 export interface Action{
 3     // 在action中只有type是必须的,让我们知道它是哪一类action(是用户点击button的action而不是异步通信的action)
 4     type: string;
 5     // 这个action可能需要带一些数据
 6     payload?: any;
 7 }
 8 // 触发action意味着状态要改变,那我们怎么修改状态呢?
 9 // 从最基本的来说,修改状态我们需要知道两样东西: 以前的状态和触发的action
10 // 那么Reducer函数有2个参数:以前的状态action和触发的action,它还要返回一个新的状态
11 export interface Reducer<T> {
12     (state: T, action: Action): T;
13 }

根据接口我们来使用一下:

 1 let reducer: Reducer<number> = (state: number, action: Action): number => {
 2     // 使用switch来区分是什么action
 3     switch(action.type) {
 4         case ‘ADD‘:
 5             return state + action.payload;
 6         case ‘DEC‘:
 7             return state - action.payload;
 8         default:
 9             // 有一些action我们不想让它改变状态,这里把原来的状态返回回去
10             return state;
11     }
12 }
13 let addAction: Action = {
14     type: ‘ADD‘,
15     payload: 5
16 }
17 let decAction: Action = {
18     type: ‘DEC‘,
19     payload: 2
20 }

可以看到,我们定义了一个怎么处理action使得状态改变的函数。还定义了一个增加的action和减少的action。

下面我们来使用一下:

let state = 10;
console.log(reducer(state, addAction)) // 15
console.log(reducer(state, decAction)) // 8

这里我们先触发了一个加5的action,这时状态变成了15,

然后我们触发了一个减2的操作,因为我们并没有保存刚才减5的后的状态,所以这时的状态还是10,最后的结果就变成了8,

这显然不是我们需要的结果,我们想把状态保存住,那么状态保存在哪里呢?我们新建一个叫作store的容器(一个对象),它用来保存状态state。

 1 // 这是一个类,用它来生成保存状态的容器,
 2 export class Store<T> {
 3     // 状态是不能让外界直接知道的
 4     private _state: T;
 5     // 生成这个容器时我们需要知道操作状态的方法
 6     constructor(private reducer: Reducer<T>) {}
 7     // 外界从容器中得到当前状态的途径
 8     public getState() {
 9         return this._state;
10     }
11     // 状态是保存在容器中的,如果外界发生了什么事情(action),想要改变状态,这时需要有一个接口让容器知道外界有需要改变状态的事件发生了
12     public dispatch(action: Action) {
13         // 以前的状态 + 触发的action = 新的状态
14         this._state = this.reducer(this._state, action);
15     }
16 }

我们来新建一个容器:

// 我们先新建一个容器
let store: Store<number> = new Store<number>(reducer);

发生action时通过容器的dispatch通知reducer去操作状态

1 store.dispatch(addAction);
2 console.log(store.getState()); // 15
3 store.dispatch(decAction);
4 console.log(store.getState()); // 13

可以看到,这里的状态就被保存住了。

上面的就是我理解的Redux保存状态的最基本的逻辑。

容器保存着状态,外界想要改变和获取状态只能通过容器。

获取状态是容易理解的,外界想要改变状态时,要通知(调用dispatch方法)容器有一个action发生了,容器内部知道发生了action,它会结合以前的状态和action产生新的状态来更新自己保存的状态。

(我觉得从代码实现上是比较简单的,但是想从原理上说清楚我是等级不够啊)

二、优化

1、action生成器

  上面的代码中,没一个action都要我们自己声明的,比如如果想定义加1的action和加2的action,可以这样写:

1 let addAction1: Action = {
2     type: ‘ADD‘,
3     payload: 1
4 }
5 let addAction2: Action = {
6     type: ‘ADD‘,
7     payload: 2
8 }

这显然不是好的体验,可以定义action生成器,把一类的action使用工厂方法生成 

1 function createAddAction(num: number): Action {
2     return {
3         type: ‘ADD‘,
4         payload: num
5     }
6 }

使用时就可以这样:

store.dispatch(createAddAction(1));
store.dispatch(createAddAction(2));

2、主动发出状态的变化

  我们现在在改变状态后是从容器中拉取状态的,有没有办法是状态改变后容器自动推送出改变呢。

  下面使用回调函数来实现容器的自动推送:

  

 1 // 这是一个类,用它来生成保存状态的容器,
 2 export class Store<T> {
 3     // 状态是不能让外界直接知道的
 4     private _state: T;
 5     private _listenCallback: ListenCallback[] = [];
 6     // 生成这个容器时我们需要知道操作状态的方法
 7     constructor(private reducer: Reducer<T>) {}
 8     // 外界从容器中得到当前状态的途径
 9     public getState() {
10         return this._state;
11     }
12     // 外界添加回调函数,在状态改变时会触发回调,让外界知道状态已经改变
13     public subscribe(listenCallback: ListenCallback) {
14         // 添加回调
15         this._listenCallback.push(listenCallback);
16         // 给外界关闭通知的接口
17         return () => {
18             this._listenCallback = this._listenCallback.filter(
19                 (l: ListenCallback) => l !== listenCallback
20             );
21         }
22     }
23     // 状态是保存在容器中的,如果外界发生了什么事情(action),想要改变状态,这时需要有一个接口让容器知道外界有需要改变状态的事件发生了
24     public dispatch(action: Action) {
25         // 以前的状态 + 触发的action = 新的状态
26         this._state = this.reducer(this._state, action);
27         // 通知外界状态改变了
28         this._listenCallback.forEach(
29             (l: ListenCallback) => l()
30         )
31     }
32 }

注意13行,这里提供了一个接口,让外界可以知道状态改变了,28行是状态改变时通知外界。

可以这样使用:

1 let unsubscribe = store.subscribe(
2     () => console.log(store.getState())
3 );
4 store.dispatch(createAddAction(1));
5
6 store.dispatch(createAddAction(1));
7 unsubscribe();
8 store.dispatch(createAddAction(1));

完整的代码可以在https://github.com/wangzting/miniRedux这里找到。

我在尝试从原理出发而不是从代码出发来解释Redux,我想知道Redux的作者是怎么思考出这个数据架构的,他是怎么把简单的道理用规范的代码表现出来的,显然,我做的不好。

任重而道远,我会继续努力。

作成:2019-02-13

修改:

  2019-02-13 添加码源地址

参考:《Angular权威教程》

原文地址:https://www.cnblogs.com/wangtingnoblog/p/Redux.html

时间: 2024-10-04 07:22:35

Redux入门之实现一个迷你版的Redux的相关文章

直播的本质(创业者应该要从商业模式的右边开始思考,你为用户创造了什么价值?找客户并不难,但要想办法让客户不离不弃;PC端功能的丰富很重要,因为手机版通常只是一个迷你版)

我想稍微给直播这件事浇点冷水. 的确,直播现在越来越火,YouTube凭着良好的基础建设平台前段时间也做起了直播,Facebook Live最近也加入了变脸.预定直播时间和双人录制的功能,更不用说国内的如火如荼:KTV在直播.电商行业在直播.金融行业在直播.不过想想以前的球赛.晚会也是直播,这并不稀奇.真正带来巨大改变的是移动端直播的兴起,让人具备了随时随地观看的可能,所以说关键是技术创新的整体“生产率提升”效应,而不是创新本身提高了“生产力”水平.我们对互联网连接一切这种文化所做的选择,结果却

60行代码实现一个迷你版Vue Router

这是一个超级精简版的VueRouter,实现hash模式下,hash改变组件切换的功能,原理就是利用了 Vue.js 的响应式机制触发router-view组件的重新渲染. 代码 https://github.com/dora-zc/vue-wheels/tree/master/MiniVueRouter 目录结构 . ├── index.html └── myVueRouter.js 路由在模板中的用法应该是下面这样: <!-- index.html --> <div id="

一个用 C 语言写的迷你版 2048 游戏,只有 500个字符

Jay Chan 用 C 语言写的一个迷你版 2048 游戏,只有 487 个字符.来围观吧 M[16],X=16,W,k;main(){T(system("stty cbreak") );puts(W&1?"WIN":"LOSE");}K[]={2,3,1};s(f,d,i ,j,l,P){for(i=4;i--;)for(j=k=l=0;k<4;)j<4?P=M [w(d,i,j++)],W|=P>>11,l*

迷你版Vue--学习如何造一个Vue轮子

项目地址 文档 数据双向绑定 Vue主流程走向 组件 nextTick异步更新 MVVM 先来科普一下MVVM的概念及原理 配套插件 mini-vuex 实现一个迷你版的vue 实现的功能 全局方法 // 继承MiniVue 产生一个新的子类构造函数 MiniVue.extend // 在实例化过程完成后运行 MiniVue.nextTick // 注册自定义指令 MiniVue.directive // 过滤器 MiniVue.filter // 组件 包括slot props MiniVue

写一个迷你模板引擎

一直想写一个模板引擎用在自己的代码上,因为之前用的一个开源的产品,每次需要变通的时候都会遇到一些局限性,不如自己写的实在,想改哪就改哪,于是今天花了一点时间造了一个很小的模板引擎,核心功能已经存在,其他的待到以后慢慢的扩充. 模板引擎说白了,就是找到页面上的占位符,然后替换掉,再插入到页面中,不管功能还是实现方法都极其简单. 占位符也就两个地方能够出现的: 文本节点 属性值 通过childNodes能够找到文本节点,通过attributes能够找到该元素下的所有存在属性值,所以请看代码,以下代码

[ASP.NET Core 3框架揭秘] 依赖注入:一个Mini版的依赖注入框架

在前面的章节中,我们从纯理论的角度对依赖注入进行了深入论述,我们接下来会对.NET Core依赖注入框架进行单独介绍.为了让读者朋友能够更好地理解.NET Core依赖注入框架的设计与实现,我们按照类似的原理创建了一个简易版本的依赖注入框架,也就是我们在前面多次提及的Cat. 源代码下载 普通服务的注册与消费泛型服务的注册与消费多服务实例的提供服务实例的生命周期 一.编程体验 虽然我们对这个名为Cat的依赖注入框架进行了最大限度的简化,但是与.NET Core框架内部使用的真实依赖注入框架相比,

(转)金蝶KIS迷你版、标准版在查询数量金额明细账时提示“发生未知错误,系统当前操作被取消,请与金蝶公司联系”

金蝶KIS迷你版.标准版在查询数量金额明细账时提示“发生未知错误,系统当前操作被取消,请与金蝶公司联系” 2013-07-10 12:17:51|  分类: 金蝶专题|举报|字号 订阅 金蝶KIS迷你版.标准版在查询数量金额明细账时提示“发生未知错误,系统当前操作被取消,请与金蝶公司的技术支持机构联系” 问题描述:厦门金蝶KIS迷你版.标准版在查询数量金额明细账时提示“发生未知错误,系统当前操作被取消,请与金蝶公司的技术支持机构联系”.但是选择“确定”后仍然可以查询到数据.问题原因: GLQty

《逻辑思维简易入门》(第2版) 阅读笔记二

<逻辑思维简易入门>(第2版) 阅读笔记二 本周阅读的是<逻辑思维简易入门>的第三章,也就是说,本书的第一部分就已经读完了. 第三章.信念的优点 信念和负信念是人们在接受一个事物时一种心理态度,延伸来说也就是对事物的认知态度.因为我们在研究 逻辑思维的时候,都有一个前提:“以正常情况以及说话者真诚”,所以有人如果对于一件事物不做回应,我们可以认为这是一种既不相信,也不怀疑的的态度. 信念的优缺点有很多,在书中主要介绍了下面几种: 1.准确性 好的信念实在准确的表达事实,同样真的信念

迷你版jQuery——zepto核心源码分析

前言 zepto号称迷你版jQuery,并且成为移动端dom操作库的首选 事实上zepto很多时候只是借用了jQuery的名气,保持了与其基本一致的API,其内部实现早已面目全非! 艾伦分析了jQuery,小钗暂时没有那个本事分析jQuery,这里就恬不知耻说说自己对zepto的源码理解,希望对各位有用 首先zepto的出现其实还是很讨巧的,他看见了巨人jQuery在移动浪潮来临时的转身慢.牵挂多的问题 马上搞出了一套轻量级类jQuery框架代码,核心代码1000行不到,快速占领了移动端的市场,