[ demo(基于react) 提取码:b36i ]
demo网盘地址
参考文档:
http://cn.redux.js.org/
1、核心概念
- state 数据
- action 是一个普通的js对象,像是描述发生了什么事的指示器,可以知道应用中发生了什么
- reducer 为了把action和state串起来的而写的函数,只是一个接收state、action,并返回新的state的函数
2、三大原则
(1)单一数据源
- 整个应用的state被存在一个object tree中,并且这个object tree 只存在唯一的一个store
- 这个优势可以用来处理:撤销,重做等复杂的功能
(2)state是只读的
- 唯一改变state的方法就是触发action
- action是一个用于描述已发生事件的普通对象
- action可被日志打印、序列化、存储、后期调试、或测试的时候回放出来
(3)使用纯函数来执行修改
- 为了描述 action 如何改变 state tree ,你需要编写 reducers
- Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state
- 控制它们被调用的顺序,传入附加数据,甚至编写可复用的 reducer
3、Action
(1)定义及注意点
- 是把数据从应用传到store的有效载荷
- 是store数据的唯一来源,通过
store.dispatch()
将action传到store - Action 本质是js 普通对象,约定action 内必须使用字符串类型的 type 字段来表示将要执行的动作
- 应该尽量减少在 action 中传递的数据,可使用index作为唯一标识,比传递整个对象要好
(2)写法
添加新 todo 任务的 action 是这样的:
const ADD_TODO = 'ADD_TODO'
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
当应用规模越来越大时,建议使用单独的模块或文件来存放 action。
import { ADD_TODO, REMOVE_TODO } from '../actionTypes'
4、Action创建函数
(1)定义
- action创建函数,就是生成action的函数
- redux中的action创建函数,只是返回一个action
(2)写法
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
(3)使用
redux中只需要把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);
store 里能直接通过 store.dispatch() 调用 dispatch() 方法,
但是多数情况下你会使用 react-redux 提供的 connect() 帮助器来调用。
bindActionCreators() 可以自动把多个 action 创建函数 绑定到 dispatch() 方法上。
5、Reducer
(1)定义
- reducer指定应用状态的变化如何响应action并发送到store
- action只是描述了有事情发生这一事实,并没有描述应用如何更新state
(2)设计state结构
- 尽可能地把 state 范式化,不存在嵌套
- 所有数据放到一个对象里,每个数据以 ID 为主键,不同实体或列表间通过 ID 相互引用数据
(3)action处理
- reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state。
;(previousState, action) => newState
- 重点
谨记 reducer 一定要保持纯净。
只要传入参数相同,返回计算得到的下一个 state 就一定相同。
没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。
reducer 中不能做的:
修改传入参数;
执行有副作用的操作,如 API 请求和路由跳转;
调用非纯函数,如 Date.now() 或 Math.random()。
写法:当state没有初始值时,可以赋值(es6的默认参数写法)
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() 新建了一个副本。
- 在 default 情况下返回旧的 state。遇到未知的 action 时,一定要返回旧的 state。
处理多个action处理:引入action、扩展reducer、拆分写法(代码参考reducers文件夹)
- 每个 reducer 只负责管理全局 state 中它负责的一部分。
- 每个 reducer 的 state 参数都不同,分别对应它管理的那部分 state 数据。
6、store
(1)定义
Store 就是把 ‘ 使用 reducers 来根据 action 更新 state ’’ 联系到一起的对象。
Store 职责:
- 维持应用的 state;
- 提供 getState() 方法获取 state;
- 提供 dispatch(action) 方法更新 state;
- 通过 subscribe(listener) 注册监听器;
- 通过 subscribe(listener) 返回的函数注销监听器。
- 再次强调一下 Redux 应用只有一个单一的 store。
(2)根据已有的 reducer 来创建 store
import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)
createStore() 的第二个参数是可选的, 用于设置 state 初始状态。
let store = createStore(todoApp, window.STATE_FROM_SERVER)
(3)发起action
这段代码用于测试数据处理逻辑
import {
addTodo,
toggleTodo,
setVisibilityFilter,
VisibilityFilters
} from './actions'
// 打印初始状态
console.log(store.getState())
// 每次 state 更新时,打印日志
// 注意 subscribe() 返回一个函数用来注销监听器
const unsubscribe = store.subscribe(() => console.log(store.getState()))
// 发起一系列 action
store.dispatch(addTodo('Learn about actions'))
store.dispatch(addTodo('Learn about reducers'))
store.dispatch(addTodo('Learn about store'))
store.dispatch(toggleTodo(0))
store.dispatch(toggleTodo(1))
store.dispatch(setVisibilityFilter(VisibilityFilters.SHOW_COMPLETED))
// 停止监听 state 更新
unsubscribe()
7、数据流
严格的单向数据流是 Redux 架构的设计核心。
Redux 应用中数据的生命周期(4个步骤)
- 1、调用 store.dispatch(action)
action 理解成新闻的摘要。可以在任何地方调用 store.dispatch(action),包括组件中、XHR 回调中、甚至定时器中。
{ type: 'LIKE_ARTICLE', articleId: 42 }
{ type: 'FETCH_USER_SUCCESS', response: { id: 3, name: 'Mary' } }
{ type: 'ADD_TODO', text: 'Read the Redux docs.' }
- 2、Redux store 调用传入的 reducer 函数。
Store 会把两个参数传入 reducer: 当前的 state 树和 action。例如,在这个 todo 应用中,根 reducer 可能接收这样的数据:
// 当前应用的 state(todos 列表和选中的过滤器)
let previousState = {
visibleTodoFilter: 'SHOW_ALL',
todos: [
{
text: 'Read the docs.',
complete: false
}
]
}
// 将要执行的 action(添加一个 todo)
let action = {
type: 'ADD_TODO',
text: 'Understand the flow.'
}
// reducer 返回处理后的应用状态
let nextState = todoApp(previousState, action)
- 3、根 reducer 应该把多个子 reducer 输出合并成一个单一的 state 树。
根 reducer 的结构完全由你决定。Redux 原生提供combineReducers()辅助函数,来把根 reducer 拆分成多个函数,用于分别处理 state 树的一个分支。
下面演示 combineReducers() 如何使用。假如你有两个 reducer:一个是 todo 列表,另一个是当前选择的过滤器设置:
function todos(state = [], action) {
// 省略处理逻辑...
return nextState
}
function visibleTodoFilter(state = 'SHOW_ALL', action) {
// 省略处理逻辑...
return nextState
}
let todoApp = combineReducers({
todos,
visibleTodoFilter
})
当你触发 action 后,combineReducers 返回的 todoApp 会负责调用两个 reducer:
let nextTodos = todos(state.todos, action)
let nextVisibleTodoFilter = visibleTodoFilter(state.visibleTodoFilter, action)
然后会把两个结果集合并成一个 state 树:
return {
todos: nextTodos,
visibleTodoFilter: nextVisibleTodoFilter
}
虽然 combineReducers() 是一个很方便的辅助工具,你也可以选择不用;你可以自行实现自己的根 reducer!
- 4、Redux store 保存了根 reducer 返回的完整 state 树。
这个新的树就是应用的下一个 state!所有订阅 store.subscribe(listener) 的监听器都将被调用;监听器里可以调用 store.getState() 获得当前 state。