【笔记系列】Redux 的思想和食用方式

458 阅读5分钟

Redux

  • 核心源码很简洁
  • 解决了多交互、多数据源的数据处理问题
  • 应用场景:组件状态共享、一个组件需要改变全局状态、一个组件需要改变另一个组件的状态
  • 类似 vuex,实际上是一个数据的中转区

问题

  • 我们可以发现,共享使用一个对象数据的话,里面共享状态可以被任意修改,程序的行为变得非常不可预料。因此我们提高了修改数据的门槛:每个人都必须通过dispatch来执行修改,并且需要大张旗鼓地向它传入一个动作对象action
  • 后来发现每次修改数据都得手动渲染:使用 sub-pub 模式,通过store.subscribe订阅数据渲染事件,每次数据更新自动渲染视图
  • 又发现了“重新渲染视图”的性能不好,因此引入了“共享结构的对象”来解决问题。只需要在每个渲染函数开头进行简单的判断,避免重复渲染没变的数据。
  • 优化stateChanger为 reducer,定义 reducer 为纯函数,功能是负责初始化state以及根据stateaction计算具有共享结构的新的state

Redux 的存在就是用来解决存取共享数据并提供修改数据的 API 这个问题的。

核心概念

store:存储容器 state:存储基本的数据快照。 action:更新 state 中的数据,需要发起一个 action reducer:将 action 和 state 串联起来,使得我们能够对一个 state 进行一个 action 并返回这个新的 state。

根据 action 对象来更新 state,这就是 Redux 思想的全部。

三大原则

  1. 单一数据源:整个应用的 state 被存储在一颗 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
  2. State 是只读的:改变 state 的唯一方法就是触发 action,action 是一个用于描述易发生事件的普通对象。
  3. 使用纯函数来修改(参数为 state 和 action):为了描述 action 如何改变 state tree,你需要编写 reducers。它接受先前的 state 和 action,返回新的 state,即 (state, action) => state

Redux 设计思想

  1. Web 应用应该是一个状态机:视图和状态是一一对应的
  2. 所有的状态,保存在一个对象中

基本API

Store

存储数据的那个对象,可以看作是一个容器,整个应用只能有一个 Store。

import {createStore} from 'redux'
const store = createStore(fn) // store 是一个对象,有两个 key,getState 和 dispatch,其对应 value 都是一个函数(getState()无参,dispatch(action) 参数为描述动作的 JS 对象)
  • fn是一个叫 stateChanger 的函数,参数列表为 (state, action),用来 对当前 state 执行 action
  • createStore 返回对象形如 { getState: ()=> state, dispatch: (action) => stateChanger(state, action) }

State

代表某一时间节点的数据快照。可以通过store.getState()拿到当前时刻的 State。

Action

State 的变化需要通过 Action 来进行操作。一个 Action 通常含有一个名称 type 以及一个携带的信息 payload

  • 为了简化 Action 的生成,可以用一个返回 Action 对象的函数来处理,这个函数叫 Action Creator

store.dispatch()

是 View 中发出 Action 的唯一方法。通过发布订阅模式来使数据更新并使页面渲染更新。

reducer()

Store 收到 Action 后,通过 State 和 Action 两者计算出新的 State,若没有收到 State,将会返回一个初始数据。

reducer 是一个纯函数,因此同样的 State 和 Action 必然得到相同的结果。

store.subscribe()

  • Store 允许使用 store.subscribe 方法设置监听函数,只要 State 一变化,就会自动执行这个函数。因此只要将组件的渲染函数(render/setState)放入到 listener,就会实现页面的自动渲染。
  • store.subscribe 返回一个函数,调用这个函数就可以解除监听。

React.js 的 context

一个组件可以通过 getChildContext 方法返回一个对象,该对象定义了子组件树的 context,提供 context 的组件必须提供 childContextTypes 作为 context 的声明和验证。子组件可以通过 this.context.<key> 来访问这个对象中的数据。

pure function 纯函数

一个函数的返回结果只依赖于它的参数,并且在执行过程中没有副作用,这个函数就叫做纯函数。

  1. 返回结果只依赖于自己的参数
  2. 执行过程里没有副作用
  • 纯函数很严格,我们几乎除了计算数据以外什么都不能干,计算的时候还不能依赖参数以外的数据。
  • 意义:非常靠谱,不会发生不可预料的行为,也不会对外部产生影响。方便调试、测试。

共享结构的对象

ES6 的拓展运算符可以用于对象的浅拷贝。

const obj = { a: 1, b : 2 }
const obj2 = { ...obj, b : 3, c: 4 } // { a: 1, b : 3, c: 4 },新 b 覆盖了旧 b

编写 reducer 习惯

  1. 定义 action types
  2. 编写 reducer
  3. 跟这个 reducer 相关的 action creators

Redux 模板写法

// 定义一个 reducer
function reducer (state, action) {
    /* 初始化 state 和 switch case */
}

// 生成 store
const store = createStore(reducer)

// 监听数据变化,重新渲染页面
store.subscribe(() => renderApp(store.getState()))

// 首次渲染页面
renderApp(store.getState())

// 后面可以随意 dispatch 一些 action 了,页面自动更新
store.dispatch(...)

参考:react.js 小书