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 ...
随机推荐
- Flutter 异常处理之图片篇
背景 说到异常处理,你可能直接会认为不就是 try-catch 的事情,至于写一篇文章单独来说明吗? 如果你是这么想的,那么本篇说不定会给你惊喜哦~ 而且本篇聚焦在图片的异常处理. 场景 学以致用,有 ...
- 事务及其特性ACID
一.事务的定义 事务是一组单元化的操作,这组操作可以保证要么全部成功,要么全部失败(只要有一个失败的操作,就会把其他已经成功的操作回滚). 一般所说的数据库事务,它是访问并可能更新数据库中各种数据项的 ...
- SQL server 表信息查询
一.查看所有表的行数select a.name as '表名',b.rows as '表数据行数'from sysobjects a inner join sysindexes bon a.id = ...
- SqlServer中的系统数据库
SqlServer中的系统数据库有五个,平时写代码不太关注,今天一时兴起研究了一下. 1. master 记录SQL Server系统的所有系统级信息,例如:登陆账户信息.链接服务器和系统配置设置.记 ...
- OPPO X9007 升级到Android5.0 Color2.1(root版) 详细纪实
今天要做个测试,而测试APK刚好是要求最低5.0版本,正好手里有个老款手机OPPO X9007,而预装的系统是4.3,试了下虽然也能运行,但是主要功能不正常,毕竟人家APK最低要求摆在那. 反正这个手 ...
- docker的简单使用
1.下载centos镜像 docker pull centos 2.查看本地所有镜像 docker images 3.后台运行docker docker run -t -i -d centos /bi ...
- Linux集群时间同步方法
方法1.ntp 平滑同步时间 (一)确认ntp的安装 1)确认是否已安装ntp [命令] rpm –qa | grep ntp 若只有ntpdate而未见ntp,则需删除原有ntpdate.如: n ...
- .NET Core 2.1来了!
太棒了! .NET Core 2.0正式发布至今已经过去了大半年,这大半年说长不长说短不短,这段时间里,我是充分地体会到了微软的诚意,那就是认认真真打造一个优秀的开源平台.这大半年的时间里,微软一直在 ...
- FragmentTabHostUnderLineDemo【FragmentTabHost带下划线】
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 使用FragmentTabHost实现顶部选项卡(带下划线效果)展现. 效果图 代码分析 1.该Demo中采用的是FragmentT ...
- 通过数据分析告诉你北京Python开发的现状
相信各位同学多多少少在拉钩上投过简历,今天突然想了解一下北京Python开发的薪资水平.招聘要求.福利待遇以及公司地理位置.既然要分析那必然是现有数据样本.本文通过爬虫和数据分析为大家展示一下北京Py ...