Redux 简单实现(一):状态管理器

Redux 简单实现 系列文章(一):状态管理器的实现。

简单的状态管理器

我们先以一个简单的计数器为例:

1
2
3
4
5
6
7
8
// 我们定了一个状态(state),它有一个 count 属性。
let state = {
count: 1
}
// 使用状态
console.log(state.count);
// 修改状态
state.count = 2;

如上,我们实现了状态的修改与使用。但有一个问题是,当我们修改了 count 后,使用了 count 的地方并没有收到任何通知。这个时候,我们需要引入 发布-订阅模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*------count 的发布订阅者实践------*/
let state = {
count: 1
};

let listeners = [];

// 订阅
function subscribe(listener) {
listeners.push(listener);
}

function changeCount(count) {
state.count = count;
// 当 count 改变的时候,我们要去通知所有的订阅者
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener();
}
}

现在我们来尝试一下:

1
2
3
4
5
6
7
8
9
// 当 count 改变的时候,实时输出新的值
subscribe(() => {
console.log(state.count);
});

// 我们来修改下 state,当然我们不能直接去改 state 了,我们要通过 changeCount 来修改
changeCount(2); // 2
changeCount(3); // 3
changeCount(4); // 4

到目前为止,我们实现了发布与订阅。当state的数据发生变化的时候,就会通知订阅者。

但现在又有了新的问题:

  • 这个状态管理器只能管理 count,不够通用
  • 公共的代码应该封装起来

所以我们再改动一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const createStore = function (initState) {
let state = initState;
let listeners = [];

// 订阅
function subscribe(listener) {
listeners.push(listener);
}

function changeState(newState) {
state = newState;
// 通知
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener();
}
}

function getState() {
return state;
}

return {
subscribe,
changeState,
getState
}
}

我们使用 createStore 做了一次封装,接收 initState 作为初始化的状态,对外暴露了 3 个接口:

  • subscribe 订阅
  • changeState 修改状态
  • getState 获取状态

试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
let initState = {
counter: {
count: 0
},
info: {
name: '',
description: ''
}
}

let store = createStore(initState);

store.subscribe(() => {
let state = store.getState();
console.log(`${state.info.name}${state.info.description}`);
});
store.subscribe(() => {
let state = store.getState();
console.log(state.counter.count);
});

store.changeState({
...store.getState(),
info: {
name: '夏目毛球',
description: 'Redux 真好玩'
}
});

store.changeState({
...store.getState(),
counter: {
count: 1
}
});

到这里为止呢,我们就完成了一个简单的状态管理器。

有计划的状态管理器

我们利用上面的状态管理器来实现一个自增、自减的计数器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let initState = {
count: 0
}
let store = createStore(initState);

store.subscribe(() => {
let state = store.getState();
console.log(state.count);
});
// 自增
store.changeState({
count: store.getState().count + 1
});
// 自减
store.changeState({
count: store.getState().count - 1
});
// 瞎改
store.changeState({
count: 'abc'
});

前面都没什么问题,但我们发现,好好的计数器 count 突然就变成字符串了。这是因为我们没有对 count 的修改做任何的约束。

那我们分成2步来解决这个问题:

  1. 制定一个 state 修改计划,告诉 store,我的修改计划是什么。
  2. 修改 store.changeState 方法,告诉它修改 state 的时候,按照我们的计划修改。

我们来设置一个 plan 函数,接收现在的 state,和一个 action,返回经过改变后的新的 state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function plan(state, action) {
switch (action.type) {
case 'INCREMENT':
return {
...state,
count: state.count + 1
}
case 'DECREMENT':
return {
...state,
count: state.count - 1
}
default:
return state;
}
}

现在,我们把这个约束传给 store,以后所有的变化都要按照这个规范来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 增加一个参数 plan
const createStore = function (plan, initState) {
let state = initState;
let listeners = [];

function subscribe(listener) {
listeners.push(listener);
}

function changeState(action) {
// 按照我的计划修改 state
state = plan(state, action);
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener();
}
}

function getState() {
return state;
}

return {
subscribe,
changeState,
getState
}
}

现在我们再来试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let initState = {
count: 0
}
// 传入plan函数
let store = createStore(plan, initState);

store.subscribe(() => {
let state = store.getState();
console.log(state.count);
});
// 自增
store.changeState({
type: 'INCREMENT'
});
// 自减
store.changeState({
type: 'DECREMENT'
});
// 这个更改并不会生效
store.changeState({
count: 'abc'
});

其实呢,把 planchangeState 改名为 reducerdispatch,就是我们熟悉的 Redux 接口了呢!那么接下来,就用 reducerdispatch 来代替他们了。

相关链接

Redux 简单实现(二):多文件协作
Redux 简单实现(三):中间件
Redux 简单实现(四):react-redux

参考资料:

完全理解 redux(从零实现一个 redux)

React 状态管理与同构实战