为了学习redux
,仅仅实现了store
,createStore
,reducer
等部分基本功能
首先用create-react-app新建一个项目,修改public/index.html
下的页面结构
<body>
<div id=‘title‘></div>
<div id=‘content‘></div>
</body>
清空src
文件夹,新建一个index.js
,添加代码代表我们要应用的状态:
const appState = {
title: {
text: ‘Redux‘,
color: ‘red‘,
},
content: {
text: ‘学习redux‘,
color: ‘blue‘
}
}
添加渲染函数,会把上面的状态数据渲染到页面上:
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
函数 , 负责数据修改
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
dispatch({ type: ‘UPDATE_TITLE_TEXT‘, text: ‘React.js‘ }) // 修改标题文本
dispatch({ type: ‘UPDATE_TITLE_COLOR‘, color: ‘blue‘ }) // 修改标题颜色
这样就不用的担心别的函数会修改appState,至少是直接修改
原来:
现在所有的数据想修改必须经过dispatch
:
store
现在有了appState
和dispatch
,可以把他们集中到一个地方,起名叫做store
,然后构建一个createStore()
函数,用来生产这种state
和dispatch
的集合
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
用于修改数据,和以前一样
现在我的代码可以优化成:
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
就不行了,想用一种通用的方式监听数据,需要用到观察者模式:
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
的时候,监听函数都会被调用
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‘ }) // 修改标题颜色
// ...后面不管如何 store.dispatch,都不需要重新调用 renderApp
只需要subscribe
一次,后面不管怎么dispatch
,renderApp
都会被重新调用,重新渲染页面,而且观察者模式下同一块数据也可以渲染其他页面。
阶段总结
现在有了通用的createStore
,可以产生一种新定义的数据类型store
,通过getState
获取状态,dispatch
修改状态,subscribe
监听数据……
原文:https://www.cnblogs.com/moe0321/p/12334733.html