redux基础知识

项目中一直使用redux作为全局状态的管理工具,最近对其又进行了一次系统性的学习,对之前的一些理解不够的细节又有了新的认识,所以写文章来记录一下。

有同学说redux的学法繁琐还有学习的必要吗?我认为是必要的,它对react来说是很经典的状态管理工具,在它的基础上二次封装的库也层出不穷,我们的终极学习目标就是封装出一套自己的redux库,方便我们的开发。本文全部为redux官方文档加上自己在实际使用的经验。

三大基本原则

单一数据源

整个应用的state存储在一个object tree中,这个tree只存在于唯一一个store中。这个store中可以存在许多分store对应各个功能模块, 但最终都需要合并为一个巨大的store。这就是redux提供的createStore()方法传入的store可以是数组的原因。

state只读

唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。 这样确保了视图和请求都不能直接修改state, 相反他们只能表达想进行修改的意愿,也就是dispatch一个action

使用纯函数进行修改

为了描述 action 如何改变 state tree ,你需要编写 reducers。reducers是一些纯函数,他们接收旧的state和action,返回新计算后的state。 他和store一样可以在应用变大时进行拆分,每个分reducers对应不同的功能模块。

三个基本概念

Action

定义

Action一般长这样:

{
  type: 'ADD_TODO',
  text: 'Build my first Redux app'
}

没错它本质上就是一个普通的对象。其中规定必须存在一个type字段用来和reducers和一些中间件匹配,而另一个字段则由你随意发挥,就像上面 我们定义的是一个string类型的text。

那么Action是干什么的呢?打个比方来说它就起到一个运送的功能,有点像一条传送带,将特定的操作和操作的数据送到指定的地方。它是把数据从 应用传递到store的有效载荷,是store数据的唯一来源。

Action创建函数

Action 创建函数 就是生成 action 的方法。“action” 和 “action 创建函数” 这两个概念很容易混在一起,使用时最好注意区分。 它的作用也很简单,就是生成action。

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

其中的text随便你怎么写都可以。

dispatch

现在有了action,我们怎么通知store呢?一般来说我们通过store.dispatch()将action传递到store。对于一个Action创建函数来说, 我们使用dispatch(addTodo(text))即可发起一次dispatch过程。或者我们可以创建一个函数来直接进行dispatch调用。

cnost boundAddTodo = text => dispatch(addTodo(text))

然后直接在应用中调用它boundAddTodo(text)即可。

reducers

Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的, 记住 actions 只是描述了有事情发生了这一事实, 并没有描述应用如何更新 state。打个比方来说这里就有点像加工机器,来处理‘传送带’action传递来的‘原料’。

在详细介绍之前,我们需要先确定好我们需要生产出什么东西也就是store。以一个Todo应用为例,我们需要一个对列表的过滤条件,一个任务列表, 由此我们大概清楚 store 的结构:

{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    {
      text: 'study react',
        completed: true,
    },
    {
      text: 'study redux',
        completed: false
    }
  ]
}

有了store后我们就可以规划好我们的传送带也就是action了,我们可以定义如下:

/*
 * 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了。reducer就是一个纯函数,接收聚德state和action,返回新的state。

(preState,action)=>newState

之所以称之为reducer,是因为这种函数和传入Array.prototype.reduce(reducer,?initialValue)里的回调函数属于同一类型。 保持它为纯函数很重要,永远不要在reducer中做以下的操作:

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

我们将以指定state的初识状态作为开始。redux首次执行时,state为undefined,此时我们可以设置并返回应用的初始state。

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
      })
    case ADD_TODO:
        return Object.assign({}, state, {
            todos: [
                ...state.todos,
                {
                    text: action.text,
                    name: 'xxx'
                }
            ]
        })
    case TOGGLE_TODO:
        return Object.assign({}, state, {
            todos: state.todos.map((todo, index) => {
                if (index === action.index) {
                    return Object.assign({}, todo, {
                        completed: !todo.completed
                    })
                }
                  return todo
            })
        })
    default:
      return state
  }
}

这里有两个注意点:

  1. 不要修改state。使用Object.assign()新建了一个副本。
  2. 在default情况下返回旧的state。遇到位置action,一定要返回旧的state。

Store

在前面,我们学会了使用 action 来描述“发生了什么”,和使用 reducers 来根据 action 更新 state 的用法。

接下来我们就可以创建将他们联系到一起的对象。这就是createStore()的作用,传入reducers从而创建出一个store。