Redux学习笔记

秦雅逸
2023-12-01

Facebook:MVC不适合大规模应用,改用Flux Redux中文文档 通过漫画学习Redux

Redux简介

使用Redux原因

随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态),很难维护。甚至state 在什么时候,由于什么原因,如何变化已然不受控制。 ,Redux 试图让 state 的变化变得可预测。

Redux和Flux非常相似。

三大原则

1.单一数据源

整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

2.State 是只读的

惟一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。

3.使用纯函数来执行修改

为了描述 action 如何改变 state tree ,你需要编写 reducers

Redux基础

Action

Action 是把数据从应用传到 store 的有效载荷。它是 store 数据的唯一来源。

一般来说你会通过store.dispatch()  将 action 传到 store。

Action 本质上是 JavaScript 普通对象。 action 内必须使用一个字符串类型的 type字段来表示将要执行的动作。其余可自定义,可以参考 Flux 标准 Action 原则上,尽量减少在 action 中传递的数据

通常 type会被定义成字符串常量。

const ADD_TODO = 'ADD_TODO'
复制代码
{
  type: ADD_TODO,
  text: 'Build my first Redux app'
}
复制代码

当应用规模越来越大时,建议使用单独的模块或文件来存放 action。

import { ADD_TODO, REMOVE_TODO } from '../actionTypes'
复制代码

Action创建函数

Action 创建函数 就是生成 action 的方法。

function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}
复制代码

下面展示一下一个anction.js的例子

/*
 * action 类型
 */

export const ADD_TODO = 'ADD_TODO';
export const TOGGLE_TODO = 'TOGGLE_TODO'
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER'

/*
 * 其它的常量
 */

export const VisibilityFilters = {
    SHOW_ALL: 'SHOW_ALL',
    SHOW_COMPLETED: 'SHOW_COMPLETED',
    SHOW_ACTIVE: 'SHOW_ACTIVE'
}

/*
 * action 创建函数
 */

export function addTodo(text) {
    return { type: ADD_TODO, text }
}

export function toggleTodo(index) {
    return { type: TOGGLE_TODO, index }
}

export function setVisibilityFilter(filter) {
    return { type: SET_VISIBILITY_FILTER, filter }
}
复制代码

Reducer

Action 只是描述了有事情发生了这一事实,并没有指明应用如何更新 state。而这正是 reducer 要做的事情。

设计 State 结构

Redux 应用中,所有的 state 都被保存在一个单一对象中,建议在写代码前先想一下这个对象的结构。

开发复杂的应用时,不可避免会有一些数据相互引用。建议你尽可能地把 state 范式化,不存在嵌套。把所有数据放到一个对象里,每个数据以 ID 为主键,不同实体或列表间通过 ID 相互引用数据。把应用的 state 想像成数据库。参考 normalizr 文档

文档中的例子比较简单,是如下设计的

{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    {
      text: 'Consider using Redux',
      completed: true,
    },
    {
      text: 'Keep all state in a single tree',
      completed: false
    }
  ]
}
复制代码

Action 处理

reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state。 之所以称作 reducer 是因为它将被传递给 Array.prototype.reduce(reducer, ?initialValue)  方法。

(previousState, action) => newState
复制代码

永远不要在 reducer 里做这些操作:

  • 修改传入参数;
  • 执行有副作用的操作,如 API 请求和路由跳转;
  • 调用非纯函数,如 Date.now() 或 Math.random()。

只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。

明白了这些之后,就可以开始编写 reducer,并让它来处理之前定义过的 action Redux 首次执行时,state 为 undefined,此时我们可借机设置并返回应用的初始 state。

import { VisibilityFilters } from './actions'

const initialState = {
  visibilityFilter: VisibilityFilters.SHOW_ALL,
  todos: []
};

function todoApp(state, action) {
  if (typeof state === 'undefined') {
    return initialState
  }

  // 这里暂不处理任何 action,
  // 仅返回传入的 state。
  return state
}
复制代码

这里一个技巧是使用 ES6 参数默认值语法 来精简代码。

function todoApp(state = initialState, action) {
  // 这里暂不处理任何 action,
  // 仅返回传入的 state。
  return state
}
复制代码

现在可以处理 SET_VISIBILITY_FILTER。需要做的只是改变 state 中的 visibilityFilter。

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    default:
      return state
  }
}
复制代码

注意:

  1. 不要修改 state。 使用 Object.assign() 新建了一个副本。不能这样使用 Object.assign(state, { visibilityFilter: action.filter }),因为它会改变第一个参数的值。你必须把第一个参数设置为空对象。

  2. **在 default 情况下返回旧的 state。**遇到未知的 action 时,一定要返回旧的 state。

处理多个 action

 类似资料: