实现MyRedux(三)

搞一手reducer

有了前面两篇,现在有了一个通用的createStore

1
2
3
4
5
6
7
8
9
10
function createStore (state, stateChanger) {
const listeners = []
const subscribe = (listener) => listeners.push(listener)
const getState = () => state
const dispatch = (action) => {
state = stateChanger(state, action) // 覆盖原对象
listeners.forEach((listener) => listener())
}
return { getState, dispatch, subscribe }
}

那他怎么用呢?

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
let appState = {
title: {
text: 'React.js 小书',
color: 'red',
},
content: {
text: 'React.js 小书内容',
color: 'blue'
}
}

function stateChanger (state, action) {
switch (action.type) {
case 'UPDATE_TITLE_TEXT':
return {
...state,
title: {
...state.title,
text: action.text
}
}
case 'UPDATE_TITLE_COLOR':
return {
...state,
title: {
...state.title,
color: action.color
}
}
default:
return state
}
}

const store = createStore(appState, stateChanger)
...

其实可以优化一下,appStorestateChange可以合并

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
function stateChanger (state, action) {
if (!state) {
return {
title: {
text: 'React.js 小书',
color: 'red',
},
content: {
text: 'React.js 小书内容',
color: 'blue'
}
}
}
switch (action.type) {
case 'UPDATE_TITLE_TEXT':
return {
...state,
title: {
...state.title,
text: action.text
}
}
case 'UPDATE_TITLE_COLOR':
return {
...state,
title: {
...state.title,
color: action.color
}
}
default:
return state
}
}

stateChange现在既充当了获取初始化数据的功能,也充当了生成更新数据的功能,如果传入了state就更新数据,否则为初始化数据,那createStore可以优化成一个参数,因为statestateChange可以合并

1
2
3
4
5
6
7
8
9
10
11
12
function createStore (stateChanger) {
let state = null
const listeners = []
const subscribe = (listener) => listeners.push(listener)
const getState = () => state
const dispatch = (action) => {
state = stateChanger(state, action)
listeners.forEach((listener) => listener())
}
dispatch({}) // 初始化 state
return { getState, dispatch, subscribe }
}

createStore 内部的 state 不再通过参数传入,而是一个局部变量 let state = nullcreateStore 的最后会手动调用一次 dispatch({})dispatch 内部会调用 stateChanger,这时候的 statenull,所以这次的 dispatch 其实就是初始化数据了。createStore 内部第一次的 dispatch 导致 state 初始化完成,后续外部的 dispatch 就是修改数据的行为了。

stateChange可以换一个名字,就叫他reducer,不要问,问就是reducer

1
2
3
4
5
6
7
8
9
10
11
12
function createStore (reducer) {
let state = null
const listeners = []
const subscribe = (listener) => listeners.push(listener)
const getState = () => state
const dispatch = (action) => {
state = reducer(state, action)
listeners.forEach((listener) => listener())
}
dispatch({}) // 初始化 state
return { getState, dispatch, subscribe }
}

reducer

createStore接受一个叫reducer的函数,他一定是一个纯函数,他接收两个参数,一个是state,一个是action

他不能干任何事情,只能初始化和计算新的state


总结

从第一篇开始,首先发现如果共享的状态能被随意修改,那程序地行为会不可预料,所以我们要求只能通过dispatch去进行数据修改,而且必须要在action中声明,然后我们把它抽象出来成为一个createStore,它能生产store,里面有getStatedispatch让我们使用

后来发现每次修改数据都要重新渲染,而我们希望能够自动渲染视图,所以使用了订阅者模式,通过store.subscribe订阅数据修改事件,为了让它自动渲染视图

在我们使用的时候发现每次都重新渲染视图会有很大的性能问题,所以使用共享结构的对象来解决问题,而后让stateChanger更名为reducer,并让他是一个纯函数,负责初始化state,根据stateaction计算具有共享结构的新state


实现MyRedux(三)
https://moewang0321.github.io/2019/12/16/2019-12-16-Redux(三)/
作者
Moe Wang
发布于
2019年12月16日
许可协议