redux的学习实现第二课:抽离store
上一节的学习中,我们把appState和dispatch已经创建完毕。现在将其抽离出来,集中到一个单独的地方,专门用于生产这种state-dispatch对。
创建createStore的代码如下:
// createStore
function createStore(state, stateChanger) {
const getState = () => state;//初始化/获取state的函数
const dispatch = (action) => {
stateChanger(state, action);
};
return { getState, dispatch };
}
对于这一段代码,我的理解是:getState不仅是被外部调用createStore的对象用来获取state,同时也起着初始化state的功能,所有需要使用到state的地方,都需要通过createStore(initState, stateChanger).getState()来获取到state,这样单一的获取方式有助于后续debug。
用上述代码重构一下上一节的内容,如下:
let appState = {
title: {
text: '我是标题',
color: 'red'
},
content: {
text: '我是内容',
color: 'blue'
}
};
function renderTitle(title) {
const titleDOM = document.getElementById('title');
titleDOM.innerHTML = title.text;
titleDOM.style.color = title.color;
}
function renderContent(content) {
const contentDOM = document.getElementById('content');
contentDOM.innerHTML = content.text;
contentDOM.style.color = content.color;
}
function renderApp(state) {
renderTitle(state.title);
renderContent(state.content);
}
// createStore
function createStore(state, stateChanger) {
const getState = () => state;//初始化/获取state的函数
const dispatch = (action) => {
stateChanger(state, action);
};
return { getState, dispatch };
}
function stateChanger(state, action) {
switch (action.type) {
case 'CHANGE_TITLE':
state.title.text = action.text;
break;
case 'CHANGE_TITLE_COLOR':
state.title.color = action.color;
break;
case 'CHANGE_CONTENT':
state.content.text = action.text;
break;
case 'CHANGE_CONTENT_COLOR':
state.content.color = action.color;
break;
default:
break
}
}
const store = createStore(appState, stateChanger);
// 打印未修改时的state
console.log('store.getS before', store.getState());
// 首次渲染页面
renderApp(store.getState());
setTimeout(() => {
store.dispatch({ type: 'CHANGE_TITLE_COLOR', color: 'pink' });
store.dispatch({ type: 'CHANGE_CONTENT', text: 'content第二次被修改' });
// 打印已修改的state
console.log('store.getS after', store.getState());
// 修改内容之后再进行二次渲染
renderApp(store.getState());
}, 2000);
代码差别:
- 上一节的dispatch修改为stateChanger, 且参数改为两个,state以及action,不再直接接触appState
- 添加的getState方法用于获取state,让代码与appState产生联系的唯一一处地方只有创建createStore的时候
以上代码的不足之处: 我们每一次借助store.dispatch去修改内容之后,都需要手动的调用一次renderApp才能将页面整个刷新,所以我们需要在dispatch修改的时候就去更新页面,该怎么做呢? 此时就用到了观察者模式,新添加的代码如下:
// createStore
function createStore(state, stateChanger) {
let listeners = [];
const getState = () => state;//初始化/获取state的函数
// 订阅事件
const subscribe = (listener) => {
listeners.push(listener);
};
const dispatch = (action) => {
stateChanger(state, action);
//每一次在执行完stateChanger之后就将事件队列里的监听事件执行处罚
listeners.forEach((listener) => listener());
};
return { getState, dispatch, subscribe };
}
const store = createStore(appState, stateChanger);
// 订阅一次renderApp方法
store.subscribe(() => renderApp(store.getState()));
// 首次渲染页面
renderApp(store.getState());
setTimeout(() => {
store.dispatch({ type: 'CHANGE_TITLE_COLOR', color: 'pink' });
store.dispatch({ type: 'CHANGE_CONTENT', text: 'content第二次被修改' });
}, 2000);
可以看到上述代码中,setTimeout中已经删除了renderApp调用,在创建store的时候进行渲染函数的订阅就可以在每一次dispatch之后去执行订阅函数。
本节学习的完整代码如下:
let appState = {
title: {
text: '我是标题',
color: 'red'
},
content: {
text: '我是内容',
color: 'blue'
}
};
function renderTitle(title) {
const titleDOM = document.getElementById('title');
titleDOM.innerHTML = title.text;
titleDOM.style.color = title.color;
}
function renderContent(content) {
const contentDOM = document.getElementById('content');
contentDOM.innerHTML = content.text;
contentDOM.style.color = content.color;
}
function renderApp(state) {
renderTitle(state.title);
renderContent(state.content);
}
// createStore
function createStore(state, stateChanger) {
let listeners = [];
const getState = () => state;//初始化/获取state的函数
// 订阅事件
const subscribe = (listener) => {
listeners.push(listener);
};
const dispatch = (action) => {
stateChanger(state, action);
//每一次在执行完stateChanger之后就将事件队列里的监听事件执行处罚
listeners.forEach((listener) => listener());
};
return { getState, dispatch, subscribe };
}
function stateChanger(state, action) {
switch (action.type) {
case 'CHANGE_TITLE':
state.title.text = action.text;
break;
case 'CHANGE_TITLE_COLOR':
state.title.color = action.color;
break;
case 'CHANGE_CONTENT':
state.content.text = action.text;
break;
case 'CHANGE_CONTENT_COLOR':
state.content.color = action.color;
break;
default:
break
}
}
const store = createStore(appState, stateChanger);
// 订阅一次renderApp方法
store.subscribe(() => renderApp(store.getState()));
// 首次渲染页面
renderApp(store.getState());
setTimeout(() => {
store.dispatch({ type: 'CHANGE_TITLE_COLOR', color: 'pink' });
store.dispatch({ type: 'CHANGE_CONTENT', text: 'content第二次被修改' });
}, 2000);