什么是 redux? 三大原则?

什么是 redux

Redux 是一个基于 js 的全局可预测状态容器,主要用于现代前端框架中进行全局状态管理,能够在不同组件之间进行状态共享

Redux 常与 React 配合使用,但它并非只能用于 React,由于 Vue 官方推出了自己的状态管理库 Vuex,因此 Redux 很少在 Vue 中使用

Redux 的实现借鉴了 Flux, 如单项数据流。但又有别于 Flux,如全局单例 store

redux 三大原则

  1. 单一数据源

    store 必须是唯一的,全局的 state 存储在单一 store 中

  2. state 是只读的

    state 只能通过派发 action 来改变

  3. reducer 必须是纯函数

    reducer 只做一件事情,通过累积的 preState 和当前的 action 计算得出新的 state

    相同的输入必须得到相同的结果,因此 reducer 必须是一个纯函数

reducer 中不应该出现副作用,比如发请求

为什么要用 redux

React 的定义是"一个用于构建 UI 界面的 javascript 库",React 关注的点在于如何将状态转换为 UI(UI = fn(state)),在自带的状态管理方案中:

  • state: 适用于管理自身状态,也可联合 this.props.chidren 实现 callback render
  • props: 适用于父子组件传值,但父子组件嵌套过深时这种方式过于繁琐
  • context: 适用于父子组件嵌套过深和兄弟组件共享状态的场景。context 一般是作为局部的状态管理方案而不是全局的,因为 context 一旦更新,Provider 下包裹的所有子组件都会重新渲染,造成性能问题

在一个复杂的应用中,数据的流向存在跨层、反向的数据流, 在交互上也存在父子、兄弟、跨组件通信, 不利于维护

而 Redux 只需要在最外层传入 store, 内层组件即可通过 props 与 store 中的状态交互

在 Redux 中,数据的流动是单向的,store 是全局单一的,reducer 是纯函数,同样的输入得到的输出一定相同,因此状态是可预测的

什么时候用 redux

  1. 某个组件的状态,需要让其他组件可以随时拿到
  2. 一个组件需要改变另外一个组件的状态
  3. 如果你不确定要不要用 redux,那就一定不需要用 redux

redux 基本 api

createStore

createStore 是 redux 的重要组成部分, 大部分 api 都基于它生成

以下是 createStore 的形参列表

  1. reducer: 初始化、更新 state
  2. preloadedState: 默认初始化 state,但一般不用它而是 reducer 处理 init action 来初始化一个 state
  3. enhancer: 一般用于 applyMiddleWare 增加中间件,作用是对 createStore 进行增强,覆盖原来的 dispatch 来实现一些功能上的拓展,如异步 action、日志打印、异常监听

getState

获取 state 的唯一方式,返回当前最新的 state

createStore 内部维持了一个变量 currentState,这个变量是私有的,对外部隐藏,只通过唯一接口 getState 对外暴露

react-redux connect 方法底层也是通过这个 api 拿到 state

dispatch

改变 state 的唯一方式,通过 dispatch 将用户的行为以 action 的形式通知给 redux,通知 redux 把计算最新的状态并反馈给用户

如果你想在 dispatch 的前后做一些拓展功能,比如异步 action,异常收集,日志打印,建议使用中间件来做一些增强

subscribe

订阅 store 的状态变化,当且仅当 dispatch 触发状态更新之后执行入参 callback 回调函数,并返回一个取消订阅时执行的回调函数。

通常我们使用这个 api 来实现 redux 和其他 js 库通信

react-redux 底层在 Provider 中通过 store.subscribe 发起订阅,在 state 改变时,会检查子组件是否通过 connect 消费了 store 以及子组件的 props 有变化,如果这两个条件都满足,就使用最新的状态更新子组件,从而达到精确的最小粒度的 render,相对于 context 一旦数据更新就渲染所有 Provider 包裹的子组件而言,这种处理方式在性能上显然是更优的

redux combineReducers 拆分reducer

随着业务复杂度的增加,把所有状态都放在一个 reducer 中进行处理只会让代码变得更加难以维护

此时可以利用 combineReducers 拆分多个不同的 reducer,对不同业务模块的状态进行分别管理

combineReducers 接受一个 reducers obj 作为入参,你可以给不同 reducer 指定 key,这样调用 getState 时拿到的也是对应 key 的 state

如果多个组件要共享 state,使用相同的 reducer 即可

import {combineReducers, createStore} from 'redux'
import countReducer from './reducer'
import countReducer2 from './count2reducer' const store = createStore(combineReducers(myCountState:countReducer,myCountState2:countReducer2}))
// store.getState() {myCountState:{...},myCountState2:{...}} export default store

redux MiddleWare 中间件

如果要记录状态的历史变化,你会怎么做?

我们可能很容易想到只要在 dispatch 前后分别 log 一下就好了

但是一个复杂的大型项目,调用 dispatch 的地方不计其数,上述实现过于臃肿

所以我们应当使用中间件

中间件其实就是对 dispatch 的增强,中间件在 action 到达 store 的前后提供了逻辑插入点,我们可以在上面实现一些异步 action、日志输出、异常检测等功能

使用中间件

通过 applyMiddleWare 传入中间件

const store = createStore(reducer, applyMiddleware(thunk))

redux 数据流向

  1. 初始化 store,此时 redux 会 dispatch 一个 type 为 init,payload 为 undefined 的 action 并从 reducer 拿到最初的 state
  2. 组件引入 store 并通过 getState 消费 store 状态,通过 subscribe 订阅 store 状态变化
  3. 用户动作产生 action,dispatch 派发 action 到 store 并通过 reducer 更新状态。dispatch 的前后如果有中间件会在此时执行中间件相关逻辑
  4. 状态更新发布之后通知订阅者,订阅者执行注册订阅的回调函数,在回调函数中可以调用 render 将最新的状态展现给用户,并等待用户的下一次动作

最新文章

  1. 《Wireshark数据包分析实战》 - http背后,tcp/ip抓包分析
  2. 项目新的需求,网页的自适应交付/响应式交付 Responsive/Adaptive Delivery
  3. mongo 1067错误
  4. android 四种堆状态
  5. HDU2520 我是菜鸟,我怕谁
  6. 用高德地图API 通过详细地址获得经纬度
  7. ThinkPhp3.2 无法加载模块:Index
  8. PHP使用curl伪造IP地址和header信息
  9. git 使用随笔
  10. 从java的开始,java概述,java配置环境变量
  11. jq修改hover样式
  12. [angularjs] angularjs系列笔记(二)指令
  13. 关键字提取算法TF-IDF和TextRank(python3)————实现TF-IDF并jieba中的TF-IDF对比,使用jieba中的实现TextRank
  14. 利用pyecharts做地图数据展示
  15. 在eclipse中安装go编辑器阅读fabric代码
  16. ida自动编译配置
  17. Vivado神器之DocNav
  18. MyBatis 知识点
  19. PHP代码为什么不能直接保存HTML文件——>PHP生成静态页面教程
  20. [微信开发] - weixin4j关键类解析

热门文章

  1. python神器 Jupyter Notbook
  2. 解决使用DBeaver连接MySQL时报错-The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone.
  3. swing 实现用户登录注册界面(不使用数据库)
  4. 在 Java 中 CycliBarriar 和 CountdownLatch 有什么区别?
  5. springboot 设定访问项目的根路径
  6. 在 Java 中 Executor 和 Executors 的区别?
  7. 数仓建模—OneID
  8. Numpy实现多项式曲线拟合
  9. Numpy实现SVD矩阵分解
  10. 体验javascript之美6:如果你觉得什么都会了或者不知道js学什么了看这里-面向对象编程