3.1 开始使用 redux
前面我们介绍了 flux 架构以及其开源实现 redux,在这一节中,我们将完整的介绍 redux:
redux 介绍
redux 是什么
redux 概念
redux 三原则
redux Stores
redux Action
redux Reducers
redux 数据流动
3.1.1 redux 介绍
redux 是什么
Redux is a predictable state container for JavaScript apps译:Redux 是为 Javascript 应用而生的可预估的状态容器
redux 介绍
redux 是什么
redux 概念
redux 三原则
redux Stores
redux Action
redux Reducers
redux 数据流动
Redux is a predictable state container for JavaScript apps译:Redux 是为 Javascript 应用而生的可预估的状态容器
定义有些抽象,简单来讲 redux 可以理解为基于 flux 和其他一些思想(Elm,函数式编程)发展出来的前端应用架构库,作为一个前端数据状态容器体现,并可以在 React 和其他任何前端框架中使用。
redux 概念
store: 同 flux,应用数据的存储中心
action: 同 flux ,应用数据的改变的描述
reducer: 决定应用数据新状态的函数,接收应用之前的状态和一个 action 返回数据的新状态。
middleware: redux 提供中间件的方式,完成一些 flux 流程的自定义控制,同时形成其插件体系
redux 三原则
单一的 store
区别于 flux 模式中可以有多个 state,整个应用的数据以树状结构存放在一个 state 对象中。
console.log(store.getState())
/* Prints
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
*/
state 只读
state 包含的应用数据不能随意修改,修改数据的唯一方式是 dispatch action,action 描述要修改的信息(这和 flux 架构上是一致的,不过在设计上更加严格,见后面的 reducer 设计)。
store.dispatch({
type: 'COMPLETE_TODO',
index: 1
})
store.dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: 'SHOW_COMPLETED'
})
数据的改变由纯函数生成
在 redux 中,应用数据的改变路径为:
store.dispatch(action)
newState = reducer(previousState, action)
reducer 为纯函数
纯函数是函数是编程的思想,只要参数相同,函数返回的结果永远是一致的,并且不会对外部有任何影响(不会改变参数对象的数据)。也就是说 reducer 每次必须返回一个新状态,新状态由旧状态和 action 数据决定。
3.1.2 安装 redux
安装 redux 核心和 react-redux 集成进 react
$ npm install --save redux
$ npm install --save react-redux
3.1.3 redux store
在 redux 中 store 作为一个单例,存放了应用的所有数据,对外提供了如下的 API:
获取数据
store.getState()
通过触发 action 来更新数据
store.dispatch(action)
pubsub 模式提供消息订阅和取消
store.subscribe(listener)
创建并初始化 store
redux 提供 createStore
方法来创建一个 store
/**
* [createStore description]
* @param {[type]} reducer [reducer 函数]
* @param {[type]} initialState [应用初始化状态]
* @return {[type]} [description]
*/function createStore(reducer, initialState) {
// ....
}
创建一个 store :
var redux = require('redux');
var todoAppReducer = require('./reducers');
var initalState = {
todos: []
};
var store = redux.createStore(todoAppReducer, initialState);
触发 action
redux 修改应用数据的唯一方式为 store.dispatch(action)
eg:
store.dispatch({
type: 'ADD_TODO',
title: 'new todo'
})
消息订阅和取消
为了让用户监听应用数据改变,redux 在 store 集成了 pubsub 模式
订阅
// 每次应用数据改变,输出最新的应用数据
store.subscribe(function(){
console.log(store.getState())
})
// 如果新增了 todo 会触发上面的订阅函数
store.dispatch({
type: 'ADD_TODO',
title: 'new todo'
})
取消
subscribe 返回的函数即为取消函数
var unsubscribe = store.subscribe(function(){
console.log(store.getState())
})
// ....
unsubscribe();
设计应用数据结构
所有数据都存放在一个 store 中,以 todoApp 为例子,state 的数据结构可能为
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
当应用变大的时候,数据结构可能没有这么简单,这时候需要找到一个较好的结构来设计应用数据,下面是两个 redux 在设计 state 上的 tip:
业务数据和 UI 状态数据分离,尽量避免 UI 状态数据放在 store 中,即便放在 store 中也好和业务数据分离。
避免嵌套,在一个复杂的场景,数据对象可能很深,出现多层,那在设计的时候可以通过 id 的方式来引用,可以参考 normalizr的简化方式
3.1.4 redux action
我们已经知道 action 就是数据修改的描述信息,不过在实际使用过程中需要理解下面的这些规范:
action 描述数据结构
action 类型常量
action creator
action 描述数据结构
redux 对 action 对象的数据结构做了简单规范,每个对象必须包含一个字段 type,应用通过 type 来识别 action 类型,其他字段不做任何限制。
eg:
{
type: "ADD_TODO",
text: 'Build my first Redux app'
}
{
type: "REMVOE_TODO",
index: 1
}
{
type: "TOGGLE_TODO",
id: 'a1s2d1'
}
action 类型常量
为了项目的规范,通常把 action type 定义为名称常量,放在一个单独的文件中管理,这在大型项目中是一个很好的习惯。
eg:
var ADD_TODO = "ADD_TODO"
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
action creator
在 flux 模式小节已经介绍过,为了规范化 action 通过 action creator 来创建
function addTodo(text) {
return {
type: ADD_TODO,
text: text
}
}
原来的 dispatch 的使用改为了
// 旧的方式
store.dispatch({
type: ADD_TODO,
text: text
})
// 新的方式
store.dispatch(addTodo(text))
3.1.5 redux reducer
reducer 应该是最为陌生的概念,理解 reducer 是理解 redux 的关键,牢记 reducer 决定应用数据的改变
reducer 基础
/**
* [reducer description]
* @param {[type]} previewsState [之前状态]
* @param {[type]} action [redux action]
* @return {[type]} newState [新状态]
*/function reducer(previewsState, action) {
var newState;
switch(action.type) {
case ..
case ..
default:
newState = previewsState
}
return newState;
}
首先 reducer 是一个纯函数,接收到之前的应用状态和 action 并返回一个新状态,为了每次返回一个新状态,可以通过 Object.assign() 方法返回一个新的对象,也可以使用 Immutable.js (后面的章节会讲解 Immutable.js)。
function todoApp(state, action) {
state = initialState
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
default:
return state
}
}
redux 会在初始的时候调用一次 reducer (这时参数 previewsState 为 undefined), 可以借用这个机会返回应用初始状态数据。
eg:
// reducers/todoApp.jsvar initialState = {todos: ...};
function todoApp(state, action) {
if (typeof state === 'undefined') {
return initialState
}
// 返回默认值return state
}
module.exports = todoApp;
总结需要注意的点:
纯函数特性,不能修改 previewsState,不能调用异步 API,无论什么时候,传入相同的参数都能立即返回相同的结果(不能调用 Math.random, Data.now 这些函数,因为会导致不同的结果)
默认返回 previewsState (在 action 不会得到处理的情况)
处理 state 为 undefined 的情况
reducer 组合
一个 todo 应用可能有很多操作,在真实场景上面的 todoApp reducer 可能膨胀为如下的结构:
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,
completed: false
}
]
})
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
}
}
这时候可以对 todoApp reducer 做拆分,将它拆分为多个不同的 reducer,todoApp reducer 的作用只是组合其他 reducer 。
拆分后可以如下:
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
}
}
function visibilityFilter(state = SHOW_ALL, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return action.filter
default:
return state
}
}
function todoApp(state = {}, action) {
return {
visibilityFilter: visibilityFilter(state.visibilityFilter, action),
todos: todos(state.todos, action)
}
}
module.exports = todoApp
todoApp 不再负责整个应用状态的修改,而是将责任分配给了其他的 reducer, 每个子 reducer 相互独立,负责各自的责任,这个时候应用数据的改变不是靠一个 reducer 改变的,而是由多个 reducer 组合起来,这是 redux 应用的基础,叫做 reducer 组合。
可以发现 reducer 的组合和状态数据的结构是相同的,都可以理解为树状结构。 state 根对象有一个根 reducer (createState 传入的 reducer)这里是 todoApp , state 对象下面的第一级数据对应不用的 reducer,visibilityFilter 和 todos , 理解起来这也是一个典型的 分而治之策略。
对于 reducer 的组合,redux 提供了 combineReducers()
方法来简化,上面的 todoApp 可以简化为:
var todoApp = redux.combineReducers({
visibilityFilter: visibilityFilter,
todos: todos
})
这样的写法和上面的写法作用完全相同
3.1.5 redux 数据流动
redux 继承 flux 最核心的地方是 数据的单向流动,
上面已经详细介绍了 redux 的各个概念,下面将这些概念结合起来,看看数据怎么在 redux 中流动的。
第一步:调用 store.dispatch(action)
可以在任何地方触发 dispatch,例如 UI 交互,API 调用
第二步: Redux store 调用 rootReducer
redux 收到 action 过后,调用根 reducer 并返回最新的状态数据。(根 reducer 内部组合其他 reducer 返回部分的最新状态)
第三步:接收新状态并 publish 给订阅者
当 rootReducer 返回最新的状态后,通知订阅函数 store.subscribe(listener)
。在 React 中,可以订阅状态更新,在订阅函数中获取最新的状态过后,修改根组件的数据:
component.setState(newState)
3.1 开始使用 redux的更多相关文章
- RxJS + Redux + React = Amazing!(译一)
今天,我将Youtube上的<RxJS + Redux + React = Amazing!>翻译(+机译)了下来,以供国内的同学学习,英文听力好的同学可以直接看原版视频: https:/ ...
- 通过一个demo了解Redux
TodoList小demo 效果展示 项目地址 (单向)数据流 数据流是我们的行为与响应的抽象:使用数据流能帮我们明确了行为对应的响应,这和react的状态可预测的思想是不谋而合的. 常见的数据流框架 ...
- RxJS + Redux + React = Amazing!(译二)
今天,我将Youtube上的<RxJS + Redux + React = Amazing!>的后半部分翻译(+机译)了下来,以供国内的同学学习,英文听力好的同学可以直接看原版视频: ht ...
- redux学习
redux学习: 1.应用只有一个store,用于保存整个应用的所有的状态数据信息,即state,一个state对应一个页面的所需信息 注意:他只负责保存state,接收action, 从store. ...
- webpack+react+redux+es6开发模式
一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...
- Redux初见
说到redux可能我们都先知道了react,但我发现,关于react相关的学习资料很多,也有各种各样的种类,但是关于redux简单易懂的资料却比较少. 这里记录一下自己的学习理解,希望可以简洁易懂,入 ...
- react+redux教程(八)连接数据库的redux程序
前面所有的教程都是解读官方的示例代码,是时候我们自己写个连接数据库的redux程序了! 例子 这个例子代码,是我自己写的程序,一个非常简单的todo,但是包含了redux插件的用法,中间件的用法,连接 ...
- react+redux教程(七)自定义redux中间件
今天,我们要讲解的是自定义redux中间件这个知识点.本节内容非常抽象,特别是中间件的定义原理,那多层的函数嵌套和串联,需要极强逻辑思维能力才能完全消化吸收.不过我会多罗嗦几句,所以不用担心. 例子 ...
- react+redux教程(六)redux服务端渲染流程
今天,我们要讲解的是react+redux服务端渲染.个人认为,react击败angular的真正“杀手锏”就是服务端渲染.我们为什么要实现服务端渲染,主要是为了SEO. 例子 例子仍然是官方的计数器 ...
- react+redux教程(五)异步、单一state树结构、componentWillReceiveProps
今天,我们要讲解的是异步.单一state树结构.componentWillReceiveProps这三个知识点. 例子 这个例子是官方的例子,主要是从Reddit中请求新闻列表来显示,可以切换reac ...
随机推荐
- java中 抽象类和接口的区别
一. 什么是抽象类及什么是抽象方法 抽象方法是一种特殊的方法:他只有声明,而没有具体实现,抽象方法的声明格式为: abstract void funName(); 抽象方法必须用 abstract 修 ...
- ubuntu 18.04 自启动
按下面二种方式打开自启动设置窗口,设置启动参数:(两种方式) 方式一:在桌面左上角的搜索框中输入Startup Applications,打开,点击Add,Name处填open_terminal(自定 ...
- Codeforces 578B "Or" Game (前缀和 + 贪心)
Codeforces Round #320 (Div. 1) [Bayan Thanks-Round] 题目链接:B. "Or" Game You are given \(n\) ...
- C. Ancient Berland Circus(三点确定最小多边形)
题目链接:https://codeforces.com/problemset/problem/1/C 题意:对于一个正多边形,只给出了其中三点的坐标,求这个多边形可能的最小面积,给出的三个点一定能够组 ...
- Java finally块
try块也可以有零个或一个finally块. finally块总是与try块一起使用. 语法 finally块的语法是: 1 2 3 finally { // Code for finall ...
- filter 在CSS用的效果
滤镜说明: Alpha:设置透明层次 blur:创建高速度移动效果,即模糊效果 Chroma:制作专用颜色透明 DropShadow:创建对象的固定影子 FlipH:创建水平镜像图片 FlipV:创建 ...
- 关系型数据的分布式处理系统:Cobar
Cobar简介 Cobar是关系型数据的分布式处理系统,它可以在分布式的环境下像传统数据库一样为您提供海量数据服务. Github:https://github.com/alibaba/cobar 整 ...
- CTU OPEN 2017 Pond Cascade /// 思维
题目大意: 给定N F 给定N个水池的大小 每个水池都以流量F开始注水 当位置较前的水池注满后 水会溢出到下一个水池 求 最后一个水池开始溢出的时间 和 所有水池全部注满的时间 1.最后一个n水池开始 ...
- position: relative 和 position: absoution 的详解
position属性指定一个元素(静态的,相对的,绝对或固定)的定位方法的类型 relative:生成相对定位的元素,相对于其正常位置进行定位. 对应下图的偏移 absolute: 生成绝对定位的元素 ...
- python之命名元组的好处
collections.namedtuple() 命名元组的一个主要用途是将你的代码从下标操作中解脱出来举例使用 # 使用 from collections import namedtuple Sub ...