redux 简介
概述
Redux 本身是个极其简单的状态管理框架, 它的简单体现在概念少, 流程明确. 但是, 正是因为简单, 使用上没有强制的约束, 所以如果用不好, 反而会让状态管理更加混乱.
我觉得, 用好 Redux, 首先必须了解其中的几个基本概念, 不只是看看文档, 了解它们的定义, 关键是理解其和整个状态管理的关系. 其次, 要能将这些概念对应到具体的业务系统中, 通过这些概念来规划业务系统的状态管理.
Redux 核心和原则
Redux 核心或者说目的一句话就能概括, 清晰的描述应用的状态 . Redux 提供的 API 没有什么神奇之处, 只是为了实现这个核心而提供的一些工具而已, 不用这些 API 一样能实现 Redux 的状态管理.
Redux 提出的所谓 3 条原则, 其实并不是框架中的约束, 框架对于你如何组织代码其实是极其自由的. 这 3 条原则, 是在自己组织状态管理时, 需要时时记在心里, 自己来控制代码不要违反原则.
- 这个应用的状态是一个唯一的状态树
- 状态是只读的, 只能通过 action 来触发修改, 其实实际修改状态的是 reducer
- 修改状态只能通过纯函数
Redux 中的概念
了解 Redux 中的基本概念, 有助于组织出符合 redux 风格的状态管理, 仅仅使用 redux API 来管理状态, 并不是真正的 redux. Redux 中的概念主要有:
- state
- reducer
- action
其他都是基于这 3 个概念衍生出来的, 这 3 个概念和 Redux 的状态处理流程息息相关.
data flow
严格的单向数据流 , 整个 Redux 都是围绕它来组织的.
单向的数据流, 明确每次 state 的改变, 确保整个应用的状态变化清晰, 可追溯.
reducer
reducer 就是实际改变 state 的函数, 在 redux 中, 也只有 reducer 能够改变 state. 这时再看 redux 的 3 个原则, 我们在组织代码时必须确保 reduer 是纯函数.
根据 redux 的原则, 整个应用只有一个唯一的状态树, 这样, 理论上只要一个 reducer 就够了. 但是, 实际编码时, 如果应用稍具规模, 只有一个 reducer 文件, 显然不利于分模块合作开发, 也不利于代码维护.
所以, reduer 一般是按模块, 或者根据你所使用的框架来组织, 会分散在多个文件夹中. 这时, 可以通过 redux 提供的 API combineReducers 来合并多个 reducer, 形成一个唯一的状态树.
reducer 的使用只要注意 2 点:
- 必须是纯函数
- 多个 reducer 文件时, 确保每个 reducer 处理不同的 action, 否则可能会出现后面的 reducer 被覆盖的情况
state
state 或者说是 store, 其实就是整个应用的状态. 其中, 哪些内容是 state? 哪些不是? 哪些要放在 state 中管理? 哪些由页面自己管理? 等等是关键. state 定义的好坏直接影响了应用的维护性和扩展性, 反而是用哪种状态管理框架无关紧要.
很多应用其实是用了很前沿的状态管理技术或者框架, 花了很多时间去熟悉框架的使用, 理解框架的概念, 但是却没有好好定义应用的状态, 被管理的状态如果本身就乱, 管理的再好也没用.
一般, 状态管理框架都有个 计算属性 的概念, redux 也有, 计算属性 在 redux 中的实现有 2 种方式,
- 通过 reducer, 定义一些计算属性(本质上还是 state), 在改变 state 的时候同时修改这些计算属性.
- 通过 selector, 这个是专门用于 redux 库的 selector. reselect
第二种方案是比较通用的, 也是后面示例中使用的方式
action
redux 中的 action 其实就是一个 包含 type 字段的 plain object. type 字段决定了要执行那个 reducer, 其他字段作为 reducer 的参数.
action creator
action creator 本质是一个函数, 返回值是一个满足 action 的定义的 plain object. 使用 action creator 的目的就是简化 action 的定义, 比如:
const action1 = { type: 'CONNECT', palyload: {user: 'xxx', passwd: 'yyy'}}
const action2 = { type: 'CONNECT', palyload: {user: 'zzz', passwd: 'aaa'}}
这种情况下, 就可以用 action creator 来简化:
const actionCreator = (payload) => ({type: 'CONNECT', payload})
async action
redux 本身的 action 都是是同步的, 但是可以通过如下方法完成异步操作.
const asyncAction = (payload, dispatch) => {
fetchAPI('xxx_url')
.then(result => dispatch({type: 'SUCCESS', payload: result}))
.catch(e => dispatch({type: 'FAILED', error: e}))
}
// call async action
asyncAction(payload, store.dispatch)
这样虽然可以完成异步的操作, 但是使用上看起来已经不是 redux 风格的 action 了. 为了保持同步和异步的一致性, 可以通过 middleware 的方式来让 redux 支持异步的 action.
middleware
redux 的 middleware 发生在 dispatching an action 和 reaches the reducer 之间. 在这个时间点, 除了可以实现异步操作, 还可以实现 logging, 路由, 崩溃报告等等.
用 middleware(redux-thunk) 改造上面的异步操作, 让其看起来和其他 redux action 一样.
// createStore 时 applyMiddleware
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
const store = createStore(
rootReducer,
applyMiddleware(thunk)
)
const asyncAction = (payload) => {
return dispatch => {
fetchAPI('xxx_url')
.then(result => dispatch({type: 'SUCCESS', payload: result}))
.catch(e => dispatch({type: 'FAILED', error: e}))
}
}
// call asyncAction
store.dispatch(asyncAction(payload))
asyncAction 的功能和上面一样, 只是使用上更像 redux. redux-thunk middleware 没有什么神奇之处, 它所做的事情就是在 dispatch(function) 的时候, 把 dispatch 作为参数传给 function. 不用 redux-thunk, 只能 dispatch(object), 这里的 object 是描述 action 的 plain object.
Redux 示例(包含计算属性)
完整的示例参见: redux-sample
action:
export const CHANGE_NUM = 'CHANGE_NUM'
export const actionChangeNumCreator = num => {
return {
type: CHANGE_NUM,
num
}
}
reducer:
import { CHANGE_NUM } from '../actions/action'
export const multiReducer = (state = { num: 1 }, action) => {
switch (action.type) {
case CHANGE_NUM:
return { num: action.num }
default:
return state
}
}
selector:
import { createSelector } from 'reselect'
export const mult2Num = createSelector(
state => state.num,
num => {
return num * 2
}
)
export const mult3Num = createSelector(
state => state.num,
num => {
return num * 3
}
)
UI 部分:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { mult2Num, mult3Num } from './selectors/selector'
import { actionChangeNumCreator } from './actions/action'
import { Input } from 'antd'
import './App.css'
class App extends Component {
render() {
const { num, num2, num3, changeNum } = this.props
return (
<div className="App">
<Input defaultValue={num} onChange={changeNum} />
<p>num*2 = {num2}</p>
<p>num*3 = {num3}</p>
</div>
)
}
}
const mapStateToProps = state => {
return {
num: state.num,
num2: mult2Num(state),
num3: mult3Num(state)
}
}
const mapDispatchToProps = dispatch => {
return {
changeNum: e => {
dispatch(actionChangeNumCreator(e.target.value))
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
补充-thunk 介绍
thunk 的概念来源于函数式编程, thunk 本身其实就是一个函数, 这个函数会在某个其他函数执行时才执行. thunk 最主要的用途就是延迟某些操作的执行.
redux-thunk middleware 也就是插入一个函数, 这个函数在 dispatch(function)时执行. 上面 asyncAction 的例子中, 这个 thunk 函数就是:
dispatch => {
fetchAPI('xxx_url')
.then(result => dispatch({type: 'SUCCESS', payload: result}))
.catch(e => dispatch({type: 'FAILED', error: e}))
}
redux 简介的更多相关文章
- React从入门到放弃之前奏(3):Redux简介
安装 npm i -S redux react-redux redux-devtools 概念 在redux中分为3个对象:Action.Reducer.Store Action 对行为(如用户行为) ...
- Flux架构与Redux简介
Flux架构区别于传统的MVC架构 在facebook实践中, 当用户接收到新消息时,右上角会弹出你有一条新消息, 右下角的对话框也会提示有新消息, 如果用户在对话框中查看了新消息,那么右上角的这个新 ...
- Redux教程1:环境搭建,初写Redux
如果将React比喻成士兵的话,你的程序还需要一位将军,去管理士兵(的状态),而Redux恰好是一位好将军,简单高效: 相比起React的学习曲线,Redux的稍微平坦一些:本系列教程,将以" ...
- React-Native 之 redux 与 react-redux
前言 本文 有配套视频,可以酌情观看. 文中内容因各人理解不同,可能会有所偏差,欢迎朋友们联系我讨论. 文中所有内容仅供学习交流之用,不可用于商业用途,如因此引起的相关法律法规责任,与我无关,如文中内 ...
- Redux(mvc、flux、react-redux)
其他章节请看: react实战 系列 Redux 关于状态管理,在 Vue 中我们已经使用过 Vuex,在 spug 项目中我们使用了 mobx,接下来我们学习 Redux. 本篇以较为易懂的方式讲解 ...
- React.js 入门与实战之开发适配PC端及移动端新闻头条平台课程上线了
原文发表于我的技术博客 我在慕课网的「React.js 入门与实战之开发适配PC端及移动端新闻头条平台」课程已经上线了,文章中是目前整个课程的大纲,以后此课程还会保持持续更新,此大纲文档也会保持更新, ...
- immutable.js 在React、Redux中的实践以及常用API简介
immutable.js 在React.Redux中的实践以及常用API简介 学习下 这个immutable Data 是什么鬼,有什么优点,好处等等 mark : https://yq.aliyu ...
- Redux 和 React-Redux简介
先说一下,为什么会产生Redux这样的框架,我们在开发React应用时,会发现组件之间需要进行数据的交换和传递.体现在: 1. 父组件要向子组件传递数据 通过修改子组件的props 2. 兄弟组件之间 ...
- 动手实现 Redux(三):纯函数(Pure Function)简介
我们接下来会继续优化我们的 createStore 的模式,让它使我们的应用程序获得更好的性能. 但在开始之前,我们先用一节的课程来介绍一下一个函数式编程里面非常重要的概念 —— 纯函数(Pure F ...
随机推荐
- 安卓4.0以上系统怎么不用root激活XPOSED框架的方法
在大多单位的引流或业务操作中,基本上都需要使用安卓的高端技术Xposed框架,近期,我们单位购买了一批新的安卓4.0以上系统,基本上都都是基于7.0以上版本,基本上都不能够刷入root超级权限,即便是 ...
- 今年开搞了,搭建一下vue开发环境
首先-->搞了几天的SpringBoot玩的差不多了,领导直接说, 别项目组需要做前后端分离,说前端缺少人手,没有办法咯,只能硬着头皮去了, 说先学一下'vue',给我个文档让我学学,说是前半年 ...
- SQLServer之集合
集合的定义 集合是由一个或多个元素构成的整体,在SQLServer中的表就代表着事实集合,而其中的查询就是在集合的基础上生成的结果集.SQL Server的集合包括交集(INTERSECT).并集(U ...
- Ginger的第一篇博客
怀着无感的心情,没有技术的身体,写下第一篇博客作为标记. 目前应该会搞清楚数据结构上相关的操作.算法,然后用c语言实现后记录在博客. 我是有目标的咸鱼! 2019/4/19
- 关于Bulk加载模式
Bulk加载模式是Informatica提供的一种高性能数据加载模式,它利用数据库底层机制,依靠调用数据库本身提供的Utility来进行数据的加载 该方式将绕过数据库的log记录,以此提高数据库加载性 ...
- docker初体验,搭建自用的gitlab服务
一.前言 git在如日中天的版本管理系统,现在如果不是工作在git版本管理系统下,几乎都不好意思给人打招呼.现在就有现成的互联网的git服务器提供给大家使用,例如号称程序的社交网络github. 正好 ...
- Spring Boot 1.5.x 基础学习示例
一.为啥要学Spring Boot? 今年从原来.Net Team“被”转到了Java Team开始了微服务开发的工作,接触了Spring Boot这个新瓶装旧酒的技术,也初步了解了微服务架构.Spr ...
- HandlerInterceptor拦截实现对PathVariable变量的读取
Http请求拦截作用 拦截后可以修改请求体 拦截后可以作一些其它统一的操作 问题提出 对于很多时间需要拦截很多Http请求,然后去获取一些参数,这些参数可能是querystring串,也可能是路由上的 ...
- 编译安装Keepalived2.0.0
简介 Keepalived是基于vrrp协议的一款高可用软件.Keepailived有一台主服务器和多台备份服务器,在主服务器和备份服务器上面部署相同的服务配置,使用一个虚拟IP地址对外提供服务,当主 ...
- Python:鲜为人知的功能特性(上)
GitHub 上有一个名为<What the f*ck Python!>的项目,这个有趣的项目意在收集 Python 中那些难以理解和反人类直觉的例子以及鲜为人知的功能特性,并尝试讨论这些 ...