React与Redux学习记录(一)

学了react之后看了redux,一脸懵逼,先来捋一捋思路。

  • redux是一种架构模式,react-redux是将redux这种架构模式与react结合起来的一个库。所以我们先理解一下这种架构模式的基本思想,理解之后react-redux库很好上手。

  • 学习react的时候,我们要在各层父子组件直接共享某个状态,往往用到状态提升的方法,state定义在根组件中,通过props传给子组件,假如子组件需要修改状态内容,就得在定义状态函数,通过调用函数告诉父组件修改内容,很是麻烦。

  • react提供了context这个东西实现状态共享,一个组件的context只有它的子组件可以访问。(关于context的使用请自行访问react官网查询https://doc.react-china.org/)
    缺点——context 里面的数据能被随时随地随意修改,每个组件都能够改 context 里面的内容会导致程序的运行不可预料。

  • 从安全上来讲,context的用法不尽如人意;从debug角度来说,随地修改context对程序员调试负担是在是太重了。我们需要做点什么让读取或者修改context变得不那么容易。

Dispatch函数

  • redux说:那好,我们定义一个函数,只有调用这个函数才可以修改那些内容,这样你自己啥时候修改的东西自己心里应该会有点bi数了,修改context这条路上多了个门槛。
    这个函数叫做dispatch,它负责数据修改数据。(根据参数选择修改不同部分的数据)
function dispatch (action) {
  switch(action)
  {
      case "act1"://do something;
      break;
      case "act2"://...
      break;
  }
}

createStore函数

  • 有了context这个东西实现父子共享数据了,那我们把所有需要共享的东西打包到context中就好了。打包是个好东西,整理归纳好我们debug才知道往哪儿找bug。于是乎redux弄了个函数,叫createStore,它负责将对共享数据的操作打包在一起。return的东西赋给context,父子组件就都可以访问getState、dispatch函数了。
function createStore (state, stateChanger) {
  const getState = () => state
  const dispatch = (action) => stateChanger(state, action)
  return { getState, dispatch }
}

监听函数Subscribe

  • 目前我们可以获取共享数据,可以修改数据,那么问题来了,浏览器知道你修改数据了吗?你没告诉它它怎么重新渲染页面呢?我们需要引入一些个儿监听函数,告诉浏览器:“喂,看见没?我修改了,快起来渲染页面。”函数的名字叫subscribe
    我们把它打包到store里面。
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函数的用法:

function renderApp(state)
{
        //重新渲染
        const titleDOM = document.getElementById('title')
        titleDOM.innerHTML = state.text
        titleDOM.style.color = state.color
}
store.subscribe(() => renderApp(store.getState()))/*通过subscript把渲染函数push到内部的listeners容器里面*/
//...
//dispatch后,listeners内的函数会被调用,故重新渲染。

思考

性能优化问题:假设我们的页面由不同颜色、不同文字内容、不同块组成,在某个子组件中我们只修改了其中一点点东西,比如我们只修改了文章标题的颜色而已,那我们需要重新渲染整个页面吗?react的render函数只渲染被修改的部分,我们的renderApp函数是否也可以只渲染被修改部分?
答案是肯定的,不过是加几个判断的问题罢了,我们需要这样做

  • 现保存旧的状态和新的状态,一对比就知道哪里被修改了。
  • 一个坑:假如我们修改数据时,只是在原数据上进行修改,比如有state如下
state:{title:{color:red,content:"hello"}}

我们只想修改标题颜色

oldState=state
state.title.color="yellow"
newState=state

上面代码运行后,oldState===newState
原因:这是对象的引用问题,没有秒懂的同学请去恶补javascript知识。
为了让新对象与原对象不是一个对象,只能新建一个对象,把老对象的内容复制过来。es6中有个对象扩展的方法挺好用的,感兴趣的朋友可以去了解下。

Reducer函数

  • 我们再来优化,前面知道createStore函数需要两个参数state和stateChanger,我们现在嫌麻烦了,把它们合并到一起,取名reducer,这样stateChanger既充当了初始化state的角色,又可以操作数据。把东西放在一起才好找。
function reducer (state, action) {
  if (!state) {
    return {
      //........
     // 初始化state
    }
  }
  //以下写法值得借鉴(关于性能优化问题)
  switch (action.type) {
    case 'UPDATE_TITLE_TEXT':
      return {
        ...state,//es6对象扩展的方法
        title: {
          ...state.title,
          text: action.text
        }
      }
    case 'UPDATE_TITLE_COLOR':
      return {
        ...state,
        title: {
          ...state.title,
          color: action.color
        }
      }
    default:
      return state
  }
}

总结

  • 现在小A、小B、小C…都可以用我们的createStore函数了,他们只需要自己定义一个reducer函数。

  • 敲黑板,这里产生一个context,它存放共享的数据以及修改共享数据的方法,子组件不需通过props就访问到!
    套路如下:

// 定一个 reducer
function reducer (state, action) {
  /* 初始化 state 和 switch case */
}
// 生成 store
const store = createStore(reducer)
// 监听数据变化重新渲染页面
store.subscribe(() => renderApp(store.getState()))
// 首次渲染页面
renderApp(store.getState()) 
// 后面可以随意 dispatch 了,页面自动更新
store.dispatch(...)

至此,我们实现了一个简陋的redux
关于react与redux结合的问题,请听下集!

你可能感兴趣的:(javascript,react)