一步一步实现一个属于自己的Redux

说在前面

为了学习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 它会用 actiontext 字段去更新 appState.title.text;一种是 UPDATE_TITLE_COLOR,它会用 actioncolor 字段去更新 appState.title.coloraction 里面除了 type 字段是必须的以外,其他字段都是可以自定义的。

如果想要修改appState.title.text,必须调用dispatch

dispatch({ type: 'UPDATE_TITLE_TEXT', text: 'React.js' }) // 修改标题文本 dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' }) // 修改标题颜色 

这样就不用的担心别的函数会修改appState,至少是直接修改

原来:

现在所有的数据想修改必须经过dispatch


抽离出store

现在有了appStatedispatch,可以把他们集中到一个地方,起名叫做store,然后构建一个createStore()函数,用来生产这种statedispatch的集合

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一次,后面不管怎么dispatchrenderApp都会被重新调用,重新渲染页面,而且观察者模式下同一块数据也可以渲染其他页面。

阶段总结

现在有了通用的createStore,可以产生一种新定义的数据类型store,通过getState获取状态,dispatch修改状态,subscribe监听数据……

猜你喜欢

转载自www.cnblogs.com/moe0321/p/12334733.html