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