Redux系列之👉Redux设计思想

App拥有状态,所以我们使用变量appState来管理状态,组件们获取appState 渲染页面。可是组件们谁都可以修改appState 这个问题让我们难以发现appState 的改变是被谁触发的,所以我们通过一个stateChange函数来管理状态:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const appState = {
	name: 'defaultName',
}
const stateChange = action => {
	switch(action.type) {
		case 'UPDATE':
			appState.name = 'Tim';
			break;
		case 'ADD':
			appState.name = appState.name + action.addedPart;
			break;
		default:
			break;
	}
}

好的,我们现在拥有appStatestateChange函数,我们需要一个管理者 → store ,我们通过store 来获取appState 和更新appState ,于是我们声明一个生成store 的函数:

1
2
3
4
5
6
const createStore = (state, stateChange) => {
	const getState = () => state;
	const dispatch = action => stateChange(action);
	const store = { getState, dispatch };
	return store;
}

这样以后如果想获取状态可以通过store.getState(),如果想改变状态则发一个action就行store.dispatch({type: 'ADD', payload: {}})

事情进展地很顺利,可是我们似乎忘记了一个问题,store的数据改变以后,react目前是感知不到的!这时候我们很自然地想到发布-订阅模式 ,那就动手修改createStore函数吧:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const createStore = (state, stateChange) => {
	const getState = () => state;
	const listeners = [];
	const subscribe = listener => listeners.push(listener);
	const dispatch = action => {
		stateChange(action);
		listeners.forEach(listener => listener())
	};
	const store = { getState, dispatch, subscribe };
	return store;
}

之后我们便可以让React订阅store的变化了: store.subscribe(() ⇒ renderApp(store.getState()));

哦对了!上面说到的stateChange函数其实就是我们的reducer函数,后面就直接称呼reducer了!最后讨论一个话题就是如果我们dispatch一个函数,就算数据没有变,react也会被重新renderApp,这其实存在很大的性能问题。为了解决这个问题,要求reducer必须是纯函数,啊?为什么reducer必须是一个纯函数呢?OK,先介绍纯函数的特点: 相同的输入对应的输出永远不会变,没有副作用,并且不修改传入的值,所以reducer函数每次都返回一个新的state,那到底为啥要这么设计????因为如果我们真的想知道数据有没有变,只能通过深比较,而深比较真的非常耗费性能!所以我们通过限制reducer ,如果数据变了,就给我返回一个新的state,如果没变,就返回原来的state。这样通过两个state对象的地址比较(浅对比)来判断数据是否变化,性能有了巨大的提升!

多说一句,redux源码里确实是这样做的:

1
2
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
return hasChanged ? nextState : state

所以我们现在的reduer函数可以改成这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const reducer = (state = defaultState, action => {
	switch(action.type) {
		case 'UPDATE':
			return {
				...state,
				name: 'Tim',
			}
			break;
		case 'ADD':
			return {
				...state,
				name: appState.name + action.addedPart,
			}
			break;
		default:
			break;
	}
}

OK,总结,我们的store现在可以做三件事情:

  • getState
  • dispatch
  • subscribe

现在我们就可以使用redux了:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function reducer (state, action) {
  /* 初始化 state 和 switch case */
}

// 2、生成 store
const store = createStore(reducer)

// 监听数据变化重新渲染页面
store.subscribe(() => renderApp(store.getState()))

// 首次渲染页面
renderApp(store.getState()) 

// 后面可以随意 dispatch 了,页面自动更新
store.dispatch(...)
updatedupdated2020-07-062020-07-06