redux手动实现之三数据监控

接着上一节继续来看代码,每次通过 dispatch 修改状态之后都要重新调用 renderApp 去渲染,不然的话页面不会改变的,那么能不能智能一点,在每次调用 dispatch 的时候自动调用 renderApp 呢?

好像是可以的,将 renderApp 传入 dispatch ,在数据更新后,重新调用下就可以了。

但是,新的问题又来了,既然 state 是共享数据,那么使用的地方必然不止一处,如果数据更新了,需要调用的渲染函数也不止一个,dispatch 就会变得特别臃肿。

这里就要用到观察者模式了,修改 createStore 为如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
function createStore(state,stateChanger){
let events = [];
return {
subscribe:(event)=>{
events.push(event);
},
dispatch: (action) => {
stateChanger(state, action);
events.forEach((event)=>event());
},
getState: () => state
}
}

我们在 createStore 里面定义了一个数组 events和一个新的方法 subscribe,可以通过 store.subscribe(event) 的方式给 subscribe 传入一个监听函数,这个函数会被 push 到 events 中。

每次修改数据时都会调用 dispatch ,而 dispatch 除了修改数据,还会遍历调用 events 数组里面的函数,这样就可以通过 subscribe 在 events 中注册事件,来进行数据改变之后的操作。

现在只要在使用到数据的地方,通过 subscribe 注册一个事件就可以在 dispatch 触发数据改变的时候,重新渲染使用到数据的地方。

全部代码修改如下

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试</title>
</head>

<body>
<div id='title'></div>
<div id='content'></div>

<script>
const appState = {
title: {
text: "redux",
color: "red",
},
content: {
text: "redux文档内容",
color: "blue"
}
};

function stateChanger(state, action) {
switch (action.type) {
case "UPDATE_TITLE_TEXT": {
state.title.text = action.text;
break;
}
case "UPDATE_TITLE_COLOR": {
state.title.color = action.color;
break;
}
default:
break;
}
}

// 添加 createStore
function createStore(state, stateChanger) {
let events = [];
return {
subscribe: (event) => {
events.push(event);
},
dispatch: (action) => {
stateChanger(state, action);
events.forEach((event) => event());
},
getState: () => state
};
}

function renderTitle(title) {
const titleDom = document.querySelector("#title");
titleDom.innerHTML = title.text;
titleDom.style.color = title.color;
}

function renderContent(content) {
const contentDom = document.querySelector("#content");
contentDom.innerHTML = content.text;
contentDom.style.color = content.color;
}

function renderApp(appState) {
renderTitle(appState.title);
renderContent(appState.content);
}

// 生成 store
let store = createStore(appState, stateChanger);
// 监听数据变化

store.subscribe(() => {
renderApp(store.getState());
});
renderApp(store.getState());
// 三秒钟之后,修改标题和标题颜色,并重新渲染
setTimeout(function () {
store.dispatch({ type: "UPDATE_TITLE_TEXT", text: "Redux是React是好基友" });
store.dispatch({ type: "UPDATE_TITLE_COLOR", color: "green" });
// renderApp(store.getState());
}, 3000);

</script>
</body>

</html>

现在我们有了一个比较通用的 createStore,它可以产生一种我们新定义的数据类型 store,通过 store.getState 我们获取共享状态,而且我们约定只能通过 store.dispatch 修改共享状态。store 也允许我们通过 store.subscribe 监听数据数据状态被修改了,并且进行后续的例如重新渲染页面的操作。

参考

http://huziketang.mangojuice.top/books/react/