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

Posted 西门本不吹雪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 }
View Code

 

这显然不是好的体验,可以定义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权威教程》

以上是关于Redux入门之实现一个迷你版的Redux的主要内容,如果未能解决你的问题,请参考以下文章

React入门之Redux:react-redux基本使用

Flutter之Redux入门

Flutter之Redux框架入门

React入门之Redux:手动模拟Redux的基本流程

React入门之Redux:手动模拟Redux的基本流程

Flutter Fish-Redux插件入门