Installation
npm install react-redux
Using Redux
Using Redux with any UI layer requires the same consistent set of steps:
- Create a Redux store
- Subscribe to updates
- Inside the subscription callback:
- Get the current store state
- Extract the data needed by this piece of UI
- Update the UI with the data
- If necessary, render the UI with initial state
- Respond to UI inputs by dispatching Redux actions
本文选用实例:Todo
Action
1.Action(定义)
Action是把数据从应用传到store的有效载荷。它是 store 数据的唯一来源。一般来说你会通过
store.dispatch()
将 action 传到 store。
实例中的action:
// 添加新的todo
const ADD_TODO = 'ADD_TODO'
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
Action本质上是JS普通对象。我们约定,action里必须使用一个字符串类型的 type
字段来表示将要执行的动作。在小应用中,一般直接使用字符串(string)做action type。但随着应用的规模增大,建议将type
定义成字符串常量(const),并存放在单独的模块或文件里。
import { ADD_TODO, REMOVE_TODO } from '../actionTypes'
我们应该尽量减少在action中传递的数据。比如传递index
就比把整个todo对象传过去要好。
{
type: TOGGLE_TODO,
index: 5
}
最后,再添加一个action type来表示当前的任务展示选项。
{
type: SET_VISIBILITY_FILTER,
filter: SHOW_COMPLETED
}
2.Action创建函数
Action创建函数就是生成 action 的方法。
在 Redux 中的 action 创建函数只是简单的返回一个 action:
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
然后把 action 创建函数的结果传给dispatch()
方法即可发起一次 dispatch 过程。
dispatch(addTodo(text))
dispatch(completeTodo(index))
或者创建一个被绑定的action创建函数来自动 dispatch,然后直接调用:
const boundAddTodo = text => dispatch(addTodo(text))
const boundCompleteTodo = index => dispatch(completeTodo(index))
boundAddTodo(text);
boundCompleteTodo(index);
Reducer
Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。reducer用来处理之前定义过的actions。
1.设计State结构
在Redux应用中,所有state都被保存在一个单一对象中。
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
处理 Reducer 关系时的注意事项
开发复杂的应用时,不可避免会有一些数据相互引用。建议你尽可能地把 state 范式化,不存在嵌套。把所有数据放到一个对象里,每个数据以 ID 为主键,不同实体或列表间通过 ID 相互引用数据。把应用的 state 想像成数据库。这种方法在 normalizr 文档里有详细阐述。例如,实际开发中,在 state 里同时存放todosById: { id -> todo }
和todos: array<id>
是比较好的方式。
为了以最简的形式把这个对象描述出来,我们应避免在state里存放嵌套数据。可以将复杂的数据拆分为一个ID列表,和一个数据的对象(以ID为key)
2.Action处理
确定了state对象的结构后,就可以开发reducer。 reducer是一个纯函数,接受旧的state和action,返回新的state。
保持reducer纯净非常重要,永远不要在reducer里做这些操作:
- 修改传入参数;
- 执行有副作用的操作,如API请求和路由跳转;
- 调用非纯函数,如
Date.now()
或Math.random()
,
首先,指定state的初始状态。(Redux首次执行时,state为undefined
。)
// 用ES6参数默认值语法来精简代码
function todoApp(state = initialState, action) {
// 这里暂不处理任何 action,
// 仅返回传入的 state。
return state
}
然后处理action:SET_VISIBILITY_FILTER
。需要做的只是改变 state 中的 visibilityFilter
。
// 用ES7的对象展开运算符来精简代码
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return {
...state,
visibilityFilter: action.filter
}
default:
return state
}
}
Store
在前面的章节中,我们学会了使用 action 来描述“发生了什么”,和使用 reducers 来根据 action 更新 state 的用法。
Store 就是把它们联系到一起的对象。
Store有以下职责:
- 维持应用的 state;
- 提供
getState()
方法获取 state; - 提供
dispatch(action)
方法更新 state; - 通过
subscribe(listener)
注册监听器; - 通过
subscribe(listener)
返回的函数注销监听器。
Redux应用只有一个单一的store。当需要拆分数据处理逻辑时,可以使用reducer组合。
根据reducer来创建store,有多个reducer就用conbineReducers()
将他们合并为一个,然后传递createStore()
。
import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)