Redux 是十分简单的,如果有 Flux 的基础的话,当真没有任何学习难度。即使是新接触的人,也并不难。本文将官网上的基础教程整理成文档,按照教程一步步实现一个 Todo App.
Action
action是一个信息的载体,用于向 store 传输信息。也是 store 信息的唯一来源。通过 store.dispach() 来进行发布。
下面代码是 action 的一个示例
const ADD_TODO = 'ADD_TODO'
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
Action 是一个普通的 object 对象,必须有一个 type 属性用于标识是什么类型。type 是一个字符串常量,如果 type 类型很多时,可以单独放到一个文件来存储。
除了 type 外,action 中的其他的结构都可以按照自己的需求进行编写,但是原则上是数据结构和内容尽量简洁。
Action 制造器
Action 制造器:一个函数——传入参数,返回一个 action。
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
完整的 action.js
/*
* action types
*/
export const ADD_TODO = 'ADD_TODO'
export const TOGGLE_TODO = 'TOGGLE_TODO'
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER'
/*
* other constants
*/
export const VisibilityFilters = {
SHOW_ALL: 'SHOW_ALL',
SHOW_COMPLETED: 'SHOW_COMPLETED',
SHOW_ACTIVE: 'SHOW_ACTIVE'
}
/*
* action creators
*/
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 在 store 接收到 action 的时候,state 如何更改。Actions 只是告知发生了什么,但是不具体描述 state 如何更改。
设计 state
在 Redux 中,应用的所有 state 都存储在一个 object 中,编写 state 树需要尽量设计最简洁的格式。
在 todolist 一共需要做两个事:
- 过滤器
- 整个 todolist 的数据
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
处理 actions
Reducer 是一个纯函数。这个函数接收前一个 state 和 actions, 经过处理,返回一个新的 state。
(previousState, action) => newState
Reducer 不能做的事情:
- 改变参数;
- 产生副作用,例如接口请求和路由过渡;
- 请求非纯函数,例如日期和随机数函数。
相同的入参,通过计算,产生相同的返回 state,无意外,无副作用,没有 api 请求,不发生突变,就是一个纯计算函数。
首先写一个 reducer 函数,定义一个初始的 state 值,因为 redux 会首先使用一个 undefined state,我们可以通过这个契机,返回初始 state。
import { VisibilityFilters } from './actions'
const initialState = {
visibilityFilter: VisibilityFilters.SHOW_ALL,
todos: []
}
function todoApp(state, action) {
if (typeof state === 'undefined') {
return initialState
}
// For now, don't handle any actions
// and just return the state given to us.
return state
}
如果应用 ES6 的语法,可以给参数设置一个初始值,这样可以处理 state 为 undefined 的状态。代码更加简洁。
function todoApp(state = initialState, action) {
// For now, don't handle any actions
// and just return the state given to us.
return state
}
接下来,处理 SET_VISIBILITY_FILTER 类型的 action。
import {
SET_VISIBILITY_FILTER,
VisibilityFilters
} from './actions'
...
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
default:
return state
}
}
以上代码需要注意的:
- 不改变原始的 state 值,我们应用 object.assign 函数,将前一个 state 和 新改变的 state 存储在一个空对象中返回。我们同样可以应用 ES6 新语法来完成这步操作。
- 在 default 的状态下,返回前一个 state,真很重要。
在应用变得复杂的时候,我们可以通过拆分细化 reducer 函数,然后应用 redux 的一个特殊函数 combineReducers, 将细化后的 reducer 进行合并返回。
以下为官网上完整的 reducer 代码:
import { combineReducers } from 'redux'
import {
ADD_TODO,
TOGGLE_TODO,
SET_VISIBILITY_FILTER,
VisibilityFilters
} from './actions'
const { SHOW_ALL } = VisibilityFilters
function visibilityFilter(state = SHOW_ALL, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return action.filter
default:
return state
}
}
function todos(state = [], action) {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
text: action.text,
completed: false
}
]
case TOGGLE_TODO:
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: !todo.completed
})
}
return todo
})
default:
return state
}
}
const todoApp = combineReducers({
visibilityFilter,
todos
})
export default todoApp
Store
前面讲到,actions 是描述发生了什么,reducers 根据 actions 进行处理,返回新的 actions。
store 将上述变化集成起来,主要有五种作用:
- 存储 state;
- 通过 getState() 获取 state;
- 通过 dispatch(action) 更新 state;
- 通过 subscribe(listener) 注册监听;
- 处理为注册的监听。
需要注意的是,一个应用只有一个 store, 这是前面讲过的三个原则之一: 单一数据源。如果需要进行业务拆分的话,就拆分 reducer 处理函数。
可以通过上步生产的 reducer 生成需要的 store。
import { createStore } from 'redux'
import todoApp from './reducers'
const store = createStore(todoApp)
数据流
Redux 框架遵循一个严格的数据流:所有的数据都是严格单向流动的。在这个严格数据流的约束下,你的状态管理将变得更加简单,可控,可预测。
数据流一共有以下四个部分:
-
store.dispatch(action) 告诉 store 发生了什么,action 就是对这个事件的描述。
-
store 监听这个变化,并且调用 reducer 函数,处理这个变化,返回新的 state。
-
根 reducer 将多个 reducer 合并成单一的 state 树。
-
store 根据根 reducer 的返回,存储一个完整的 state 树。