说在前面
为了学习redux
,仅仅实现了store
,createStore
,reducer
等部分基本功能
现在开始忘掉一切
首先用create-react-app新建一个项目,修改public/index.html
下的页面结构
1 2 3 4
| <body> <div id='title'></div> <div id='content'></div> </body>
|
清空src
文件夹,新建一个index.js
,添加代码代表我们要应用的状态:
1 2 3 4 5 6 7 8 9 10
| const appState = { title: { text: 'Redux', color: 'red', }, content: { text: '学习redux', color: 'blue' } }
|
添加渲染函数,会把上面的状态数据渲染到页面上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function renderApp (appState) { renderTitle(appState.title) renderContent(appState.content) }
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 }
|
简单的渲染完成了,但是会有一个严重的问题,渲染数据使用的是共享的状态appState
,任何人都能修改,如果我们在渲染之前做了其他函数操作,就无法智大东会对appState
做了什么事情,也就是为什么要避免全局变量
当我们的不同组件之间需要共享数据,就会产生矛盾:
组件之间共享数据 ❗❗ 数据可能在组件内被任意修改
如果我们学习一下React团队的做法,把操作变复杂
组件之间可以共享、改动数据但是不能直接修改,必须执行我定义的某些函数
定义一个dispatch
函数 , 负责数据修改
1 2 3 4 5 6 7 8 9 10 11 12
| function dispatch (action) { switch (action.type) { case 'UPDATE_TITLE_TEXT': appState.title.text = action.text break case 'UPDATE_TITLE_COLOR': appState.title.color = action.color break default: break } }
|
这样我们对数据的操作必须 通过dispatch
函数,接收一个参数action
,它是一个普通对象,里面包含一个type
字段来生命你想干什么,dispatch
内的switch
会识别这个字段,识别成功的会对appState
进行修改
上面的 dispatch
它只能识别两种操作,一种是 UPDATE_TITLE_TEXT
它会用 action
的 text
字段去更新 appState.title.text
;一种是 UPDATE_TITLE_COLOR
,它会用 action
的 color
字段去更新 appState.title.color
。action
里面除了 type
字段是必须的以外,其他字段都是可以自定义的。
如果想要修改appState.title.text
,必须调用dispatch
1 2
| dispatch({ type: 'UPDATE_TITLE_TEXT', text: 'React.js' }) dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' })
|
这样就不用的担心别的函数会修改appState,至少是直接修改
原来:
现在所有的数据想修改必须经过dispatch
:
抽离出store
现在有了appState
和dispatch
,可以把他们集中到一个地方,起名叫做store
,然后构建一个createStore()
函数,用来生产这种state
和dispatch
的集合
1 2 3 4 5
| function createStore (state, stateChanger) { const getState = () => state const dispatch = (action) => stateChanger(state, action) return { getState, dispatch } }
|
createStore
接收两个参数,一个是表示状态的state
,另一个是stateChanger
,它用来描述应用程序状态会根据action
发生什么变化,相当于dispatch
createStore
返回一个对象,包含两个方法,getState
用于获取state
的数据,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
| et appState = { title: { text: 'React.js', color: 'red', }, content: { text: 'React.js 内容', 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 } }
const store = createStore(appState, stateChanger)
renderApp(store.getState()) store.dispatch({ type: 'UPDATE_TITLE_TEXT', text: 'React.js' }) store.dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' }) renderApp(store.getState())
|
然后我们就有个棘手的问题,怎么监控数据的变化,上述代码中,如果我们不调用renderApp
,那页面的内容是不会变化的,如果往dispatch
中加入renderApp
,那我们想通用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) => { stateChanger(state, action) listeners.forEach((listener) => listener()) } return { getState, dispatch, subscribe } }
|
在方法中定义了一个数组,还有一个subscribe
方法,可以通过这个方法传入一个监听函数,并push
到数组中,修改了dispatch
方法,当它被调用的时候,会遍历数组中的函数,一个个的调用,这就意味着我们可以通过subscribe
传入数据变化的监听函数,每次dispatch
的时候,监听函数都会被调用
1 2 3 4 5 6 7
| const store = createStore(appState, stateChanger) store.subscribe(() => renderApp(store.getState()))
renderApp(store.getState()) store.dispatch({ type: 'UPDATE_TITLE_TEXT', text: 'React.js' }) store.dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' })
|
只需要subscribe
一次,后面不管怎么dispatch
,renderApp
都会被重新调用,重新渲染页面,而且观察者模式下同一块数据也可以渲染其他页面。
阶段总结
现在有了通用的createStore
,可以产生一种新定义的数据类型store
,通过getState
获取状态,dispatch
修改状态,subscribe
监听数据……