首先我们要先知道什么是状态管理器,这玩意是干啥的?

当我们在多个页面中使用到了相同的属性时就可以用到状态管理器,将这些状态存到外部的一个单独的文件中,不管在什么时候想使用都可以很方便的获取。

react和vue有些不同,react没有自己专属的状态管理方式。它使用的其实是js相关的状态管理器。我们需要记住的是,视图可以引起状态的改变,而状态的改变会导致视图的二次渲染。

说了这么半天,那么我们在react中到底是怎样使用状态管理器的呢?

redux闪亮登场

redux的前身技术是flux,他和flux很相像,但是又不完全相同。两者都规定将模型的更新逻辑全部集中在一个层面中(Flux之中的store,redux之中的reducer);但是redux中没有dispatcher的概念,他依赖的是纯函数来做事件处理器;并且redux不回去修改你的数据,它会返回一个新的对象用于更新state状态。

首先我们先来认识一下redux中的一些属性

1、store:

保存数据/状态的地方,可以把它看成是一个容器。记得在整个应用之中只能有一个store

  1. import { createStore } from 'redux'
  2. const store = createStore(fn)

  2、state:

可以把state看成是store的实例对象,他包含了状态管理器中所有的状态,是某个时间点所有数据/状态的集合

  1. const state = store.getState()

  3、action

redux中最重要的属性之一,唯一修改store种状态的方式就是提交一个action;action是一个对象,具有type属性,通常这个type属性作为提交的关键key值

  1. const action = {
  2. type: 'ADD_TODO',
  3. payload: 'Learc Redux' //这里的payload是作为参数传入的,可以不写
  4. }

  4、store.dispatch():

提交action的唯一方式,我们就是通过这种方式将action提交到状态管理器,不管后期使用了什么其他的状态管理其工具,最终都归结与这里。

  1. store.dispatch({
  2. type: 'ADD_TODO'
  3. })

  5、reducer:

store在收到action之后,必须返回一个新的state,这样view才会发生变化,而这个state的计算过程就叫做reducer

reducer是一个函数,他接受当前的state和action,返回一个全新的state

  1. const reducer = (state = {
  2. count: 10 //给state中的属性设置初始值,因为状态管理器在每次刷新后都会清空,我们可以从本地获取上次存储的值作为初始值
  3. }, action) => {
  4. switch (action.type) {
  5. case 'REDUCE':
  6. // 这里可以看到我们根据action的type值作为key值进行查找,当dispatch的时候,就可以根据这些key值的判断查找到要执行的操作
  7. return { ...state, count: state.count - action.payload }
  8. //我们根据action的需要,在原有的state基础上,返回了一个新的state对象,没有修改原来的state
  9. case 'ADD':
  10. return { ...state, count: state.count + action.payload }
  11. default:
  12. return state;
  13. }
  14. }
  15. export default reducer

  可以看到我们返回新的state对象的方式是通过ES6中的 ... 语法,这种方式看起来是不是有点复杂,有点点low?那么我们介绍一种新的方式,首先引入immutable组件

  1. yarn add immutable -S

  这种方式其实是js实现了一种深拷贝,将原来的state对象深拷贝了一份,这样对新的state对象做出任何修改都不会影响到原来的state,是不是特别香!!!

使用方式:

  1. import { Map } from 'immutable' //引入Map函数
  2. const user = (state = Map({ //使用Map深拷贝了state
  3. isLogin: localStorage.getItem('isLogin') === 'true',
  4. token: localStorage.getItem('token') || '',
  5. adminname: localStorage.getItem('adminname') || '',
  6. role: localStorage.getItem('role') * 1 || 1
  7. }), action) => {
  8. switch (action.type) {
  9. case 'CHANGE_LOGIN_STATE':
  10. return state.set('isLogin', action.payload) //set方式写入
  11. case 'CHANGE_TOKEN':
  12. return state.set('token', action.payload)
  13. case 'CHANGE_ADMIN_NAME':
  14. return state.set('adminname', action.payload)
  15. case 'CHANGE_ROLE':
  16. return state.set('role', action.payload)
  17. default:
  18. return state
  19. }
  20. }
  21. export default user
  1. state => {
  2. return {
  3. adminname: state.getIn(['user', 'adminname']), // get方式获取值, 参数是这种形式
  4. //第一个参数的含义: user状态管理器中的
  5. //第二个参数含义: 名为adminname的状态/属性
  6. }
  7. }

6、设计状态管理器

首先根据模块化开发的思想,我们可以在一个项目中设计多个种类状态管理器,最后合并到一个里面;例如,商品的状态,用户信息的状态....

  1. import { createStore, combineReducers } from "redux";
  2. // 这个combineReducers方法就是模块化开发的关键,它帮助我们把所有分模块的状态管理器合并到一起
  3. import pro from './modules/pro'
  4. import app from './modules/app'
  5. const reducer = combineReducers({ //把分模块创建的所有reducer都集中到这里的reducer
  6. pro, app
  7. })
  8. const store = createStore(reducer)
  9. export default store

  帮助解读阶段

我们要知道一件事,当我们在某一个时间点希望获取状态或者修改状态的时候,状态管理器store会为我们生成一个state实例对象,我们可以对这个实例对象进行操作。state的变化会引起View视图的改变,但是因为用户们接触不到state,只能接触到View,所以state的变化必然也必须是由View引起的!!!而action其实就是view 发出的一个通知,当用户修改了view的时候,就会发出这个通知,告诉程序,state需要发生改变了。

  1. //我们可以这样理解,dispatch,action,store三者的关系可以看成是邮局,邮件,收信人
  2. //我们想寄一封信(action),告诉朋友(收信人),我们找到工作了,需要通过邮局(dispatch)的帮助;当邮局帮我们把邮件送到了收件人的地方时,收件人就获取到了我要传递的信息,也会做出响应的改变
  3. //我们在调用dispatch的时候,通过type(key值)找到对应的邮件送到store

  状态管理器是如何使用的呢?

  1. // 可以通过provider和connect在组件中对状态管理器进行‘链接’,链接成功之后就可以使用状态管理器中的状态和方法了
  2. // /src/xxx/index.jsx
  3. import {connect} from 'react-redux'
  4. function App (props) {
  5. ...
  6. }
  7. export default connet(mapStateToProps, mapDispatchToProps)(App)
  8. // /index.js
  9. import {Provider} from 'react-redux'
  10. import App from './App.jsx'
  11. import store './store/index.js'
  12. ReactDom.render (
  13. <React.StrictMode>
  14. <Provider store = { store }>
  15. <App />
  16. </Provider>
  17. </React.StrickMode>
  18. )
  19. //也可以使用到装饰器的高阶函数 @connect @withRouter
  20. //以往从状态树取出对应的数据,让后通过props传给组件使用通过react-redux自带的connect()方法
  21. class Home extends React.Component {
  22. //....
  23. }
  24. export default connect(state => ({todos: state.todos}))(Home);
  25. //使用装饰器的话就变成这样,好像没那么复杂
  26. @connect(state => ({ todos: state.todos }))
  27. class Home extends React.Component {
  28. //....
  29. }

  这里我们对这种方式做出讲解:

我们要链接状态管理器,首先在整个项目的入口文件index.js中引入状态store,通过Provider的方式将store作为参数传递给子组件,有点类似于祖先组件给后代组件传值的方式

其次,我们要在使用状态管理器的组件中通过connect这一个高阶函数进行连接,该高阶函数的原理是,传入函数作为参数,返回另一个函数

mapStateToProps:

从名字可以看出,是把state中的状态遍历处理,放到props中,我们就可以在函数式组件中的props参数值里面获取到state.

mapDispatchToProps:

将状态管理器中的提交方法存入到props中,这样我们就可以在组件中对状态管理器中的状态进行修改。

  1. const App = (props) => {
  2. // 组件中直接就可以通过props访问到状态管理器中的状态
  3. props.adminname
  4. props.count
  5. props.bannerList
  6. props.reduceFn
  7. ...
  8. }
  9. export default connect(
  10. // 可以看到这里就是传入两个函数,返回两个函数
  11. state => {
  12. return {
  13. adminname: state.getIn(['user', 'adminname']), //这是一种存储状态的方式,一会会讲到
  14. count: state.app.count, //参数是state,我们把app状态管理器中的count属性传递到props中的count
  15. bannerList: state.pro.bannerList,
  16. }
  17. },
  18. dispatch => {
  19. return {
  20. reduceFn () { //我们在这里定义了一个reduceFn,里面是dispatch的方法,我们在props中就可以通过reduceFn这个方法发送'REDUCE'提交的信息
  21. dispatch({
  22. type: 'REDUCE',
  23. payload: 5 //payload为参数,可以不传
  24. })
  25. }
  26. }
  27. }
  28. )(App)

  我们除了可以使用这种基本的方式修改状态意外,还可以使用一些工具

redux-thunk、redux-saga

redux-thunk的使用
  1. //在store.js之中把thunk引入并挂载到状态管理器中
  2. import { createStore, combineReducers, applyMiddleware} from 'redux'
  3. import thunk from 'redux-thunk'
  4. import app from './modules/app'
  5. import pro from './modules/pro'
  6. const reducer = combineReducers({
  7. app, pro
  8. })
  9. // 通过applyMiddleware将thunk挂载到状态管理器
  10. const store = createStore(reducer, applyMiddleware(thunk))
  11. export default store

  然后我们单独设计一个文件用来封装修改状态的方式,包含异步方式

  1. // .../store/actionCreator/pro.js
  2. // 这个文件就是专门用来触发异步操作
  3. // thunk模块执行的时候, actionCreator 函数有默认的参数为dispatch
  4. // 该dispatch 可以用来触发reducer
  5. // 有时候在触发异步的时候, 需要传递参数,这个时候,可以在函数内部返回一个 actionCreator 函数
  6. const actions = {
  7. getBannerListAction (dispatch) {
  8. fetch('http://121.89.205.189/api/banner/list')
  9. .then(res => res.json())
  10. .then(res => {
  11. dispatch({
  12. type: 'CHANGE_BANNER_LIST',
  13. payload: res.data
  14. })
  15. })
  16. },
  17. getProListAction (count) { //有参数,返回一个函数,函数参数默认为dispatch
  18. count = count || 1
  19. return function (dispatch) {
  20. fetch('http://121.89.205.189/api/pro/list?count=' + count)
  21. .then(res => res.json())
  22. .then(res => {
  23. dispatch({
  24. type: 'CHANGE_PRO_LIST',
  25. payload: res.data
  26. })
  27. })
  28. }
  29. }
  30. }
  31. export default actions

  可以把上面的步骤看成定义了一个action的对象,里面有一些提交action的dispatch,当我们要在组件中要修改状态时,可以直接在这个对象中使用函数,函数会自动发起请求,提交action。

在下面组件中的使用也可以看出来,我们dispatch(actions.getBannerListAction);其实就是提交aciton的形式,只不过我们把action修改和异步请求封装起来了

  1. import actions from './store/actionCreator/pro'
  2. const App = (props) => {
  3. // props之中可以直接访问到
  4. props.reduceFn()
  5. props.addFn()
  6. props.getBannerList()
  7. props.getProList()
  8. }
  9. const mapStateToProps = (state) => {
  10. return {
  11. count: state.app.count,
  12. bannerList: state.pro.bannerList,
  13. proList: state.pro.proList
  14. }
  15. }
  16. const mapDispatchToProps = (dispatch) => {
  17. return {
  18. reduceFn () { //通过正常方式修改状态
  19. dispatch({
  20. type: 'REDUCE',
  21. payload: 5
  22. })
  23. },
  24. addFn () {
  25. dispatch({
  26. type: 'ADD',
  27. payload: 5
  28. })
  29. },
  30. getBannerList () { //通过thunk方式修改状态
  31. dispatch(actions.getBannerListAction)
  32. },
  33. getProList () {
  34. dispatch(actions.getProListAction(2))
  35. }
  36. }
  37. }
  38. export default connect(mapStateToProps, mapDispatchToProps)(App)

  链接的方式和普通的react-redux一模一样,最后也是通过dispatch一个action的方式修改状态

react-saga的使用

安装redux-saga

  1. yarn add redux-saga immutable redux-immutable -S

  

  1. 可以把redux-sagaredux-thunk看作是一种发送dispatch的方式,在旧时代我们送信(dispatch)是通过汽车、步行;使用工具可以看成是通过动车,飞机发送信件

  

  1. import { createStore, combineReducers, applyMiddleware } from 'redux'
  2. import createSagaMiddleware from 'redux-saga'
  3. import mySaga from './mySaga' //异步操作说明
  4. import home from './modules/home'
  5. import app from './modules/app'
  6. const reducer = combineReducers({
  7. app,
  8. home
  9. })
  10. const sagaMiddleware = createSagaMiddleware() //生成saga中间件
  11. const store = createStore(reducer, applyMiddleware(sagaMiddleware))
  12. //建立链接
  13. //和thunk一样,把saga中间件挂载到状态管理器中就可以使用saga的方式修改状态了
  14. sagaMiddleware.run(mySaga)
  15. //run: 发送
  16. // 这里是封装了一个mySage函数作为修改状态的函数
  17. export default store

  接下来具体介绍saga如何修改状态

在redux-saga中,修改状态时使用Genarator函数实现的

  1. import { call, put, takeLatest } from 'redux-saga/effects'
  2. import { getBannerList, getProList } from '../api/home'
  3. // redux-saga ---> 必须与generator函数一起使用
  4. function * getBannerListAction() {
  5. const res = yield call(getBannerList) //call--调用函数
  6. yield put({
  7. type: 'CHANGE_BANNER_LIST',
  8. payload: res.data
  9. })
  10. }
  11. function * getProListAction (action){
  12. const res = yield call(getProList, action.payload)
  13. yield put({
  14. type: 'CHANGE_PRO_LIST',
  15. payload: res.data
  16. })
  17. }
  18. function * mySaga () {
  19. yield takeLatest('REQUEST_BANNER_LIST', getBannerListAction)
  20. yield takeLatest('REQUEST_PRO_LIST', getProListAction)
  21. }
  22. export default mySaga

  如果看不懂上面,别怕。看这里

  1. // mysaga文件中我们定义了发送的方式
  2. import { takeLatest } from 'redux-saga/effects'
  3. // takeLatest ---分配任务;在下方。我们自己定义了key并为其分配了事件,这些事件就是store.dispatch()函数使用的
  4. function * getProListAction (action){
  5. const res = yield call(getProList, action.payload)
  6. yield put({
  7. type: 'CHANGE_PRO_LIST',
  8. payload: res.data
  9. })
  10. }
  11. function * mySaga () {
  12. yield takeLatest('REQUEST_PRO_LIST', getProListAction)
  13. }
  14. // 我们以后再想修改状态的时候就不需要使用store.dispatch()这样修改了
  15. // 可以使用这个文件中定义的key值进行修改
  16. // 我们在组件的connect中这样定义自定义函数,直接根据key值调用这里的修改方法
  17. dispatch => {
  18. dispatch({ type: 'REQUEST_PRO_LIST'})
  19. }
  20. // put, call
  21. // call ---> 含义是调用
  22. // put ---> 含义是推,把当前的action推到下一个去执行(队列)。
  23. yield put(action)
  24. yield call(fn)

  以上就是本人结合各种文档对于React常用的状态管理器的一些理解,如果有说错的地方,还希望大家能指出,我们共同进步。

  除了以上这些状态管理器,市面上还有一些工具,MobX,Umi,Dva,这些有时间的话本人也会整理出来与大家共享。

  最后,也希望大家能从这篇文章中收获一些,共勉!

对于React各种状态管理器的解读的更多相关文章

  1. React的状态管理工具

    Mobx-React : 当前最适合React的状态管理工具   MobX 简单.可扩展的状态管理        MobX 是由 Mendix.Coinbase.Facebook 开源和众多个人赞助商 ...

  2. react的状态管理

    近两年前端技术的发展如火如荼,大量的前端项目都在使用或转向 Vue 和 React 的阵营, 由前端渲染页面的单页应用占比也越来越高,这就代表前端工作的复杂度也在直线上升,前端页面上展示的信息越来越多 ...

  3. vue状态管理器(用户登录简单应用)

    技术点:通过vue状态管理器,对已经登录的用户显示不同的页面: 一  vue之状态管理器应用 主要用来存储cookie信息 与vue-cookies一起使用 安装:npm install vue-co ...

  4. VueX状态管理器 的应用

    VueX状态管理器 cnpm i vuex axios -S 1 创建Vuex 仓库 import Vue from 'vue' import Vuex from 'vuex' vue.use(Vue ...

  5. react+redux状态管理实现排序 合并多个reducer文件

    这个demo只有一个reducer 所以合并reducer这个demo用不到 ,但是我写出来这样大家以后可以用到,很好用,管理多个reducer,因为只要用到redux就不会只有一个reducer所以 ...

  6. Mobx-React : 当前适合React的状态管理工具

    MobX 简单.可扩展的状态管理        MobX 是由 Mendix.Coinbase.Facebook 开源和众多个人赞助商所赞助的.    安装 安装: npm install mobx ...

  7. React + MobX 状态管理入门及实例

    前言 现在最热门的前端框架,毫无疑问是React. React是一个状态机,由开始的初始状态,通过与用户的互动,导致状态变化,从而重新渲染UI. 对于小型应用,引入状态管理库是"奢侈的&qu ...

  8. vue - 状态管理器 Vuex

    状态管理 vuex是一个专门为vue.js设计的集中式状态管理架构.状态?我把它理解为在data中的属性需要共享给其他vue组件使用的部分,就叫做状态.简单的说就是data中需要共用的属性.

  9. vue项目--vuex状态管理器

    本文取之官网和其他文章结合自己的理解用简单化的语言表达.用于自己的笔记记录,也希望能帮到其他小伙伴理解,学习更多的前端知识. Vuex 是什么? Vuex 是一个专为 Vue.js 应用程序开发的状态 ...

随机推荐

  1. logstash的filter之grok

    logstash的filter之grokLogstash中的filter可以支持对数据进行解析过滤. grok:支持120多种内置的表达式,有一些简单常用的内容就可以使用内置的表达式进行解析 http ...

  2. InnoDB 索引详解

    1.什么是索引 索引是存储引擎用于快速找到记录的一种数据结构. 2.索引有哪些数据结构 顺序查找结构:这种查找效率很低,复杂度为O(n).大数据量的时候查询效率很低. 有序的数据排列:二分查找法又称折 ...

  3. 谱文混排之lilypond-book

    Lilypond有自带的谱文混排的工具lilypond-book,但是作为外行,一直很难搞清楚这个操作是怎样做的.很久之前请教过别人,但介于我的个人能力,只有粗浅理解,操作不得要领.在许多信息的拼凑之 ...

  4. 解决springboot启动日志异常问题

    问题描述:springboot启动异常,启动后没有日志打印. 问题原因:slf4j日志实现重复,找不到对应实现类. 问题应对: 1. 是不是项目没起来---->打印的日志数据,到这里就不打印了, ...

  5. vue 事件监听和es6模板语法

    es6模板语法的反引号是通过左上角的飘字符弄出来了,学废了吗?

  6. 下载安装wps后去除监控

    下载wps之后发现wps一直对我的电脑进行监控,占用着我的cpu和内存,我要把它清理出去.... 控制面板→管理工具→任务计划程序→任务计划程序库,有两个wps的任务计划,可以根据属性看到文件地址 C ...

  7. 描述高频题之队列&栈

    栈和队列 全文概览 基础知识 栈 栈是一种先进后出的数据结构.这里有一个非常典型的例子,就是堆叠盘子.我们在放盘子的时候,只能从下往上一个一个的放:在取的时候,只能从上往下一个一个取,不能从中间随意取 ...

  8. 洛谷 P3215 [HNOI2011]括号修复 / [JSOI2011]括号序列(fhq-treap)

    题目链接 题意:有一个长度为 \(n\) 的括号序列,你需要支持以下操作: 将 \([l,r]\) 中所有括号变为 \(c\) 将 \([l,r]\) 区间翻转 将 \([l,r]\) 区间中左括号变 ...

  9. 自助分析工具Power BI的简介和应用

    作为一名资深的IT技术人,特别喜欢学习和尝试新技术,也勇于接受挑战,勇于创新,不仅能发现问题,更要解决实际的疑难杂症,闲暇时光也乐于分享一些技术干货.记得2017年的时候,华章出版社的编辑通过网上找到 ...

  10. jumpserver——脚本安装

    CentOS Linux release 7.7.1908 (Core) 3.10.0-1062.4.1.el7.x86_64 Initialize(){ yum update -y systemct ...