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

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

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

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

redux闪亮登场

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

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

1、store:

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

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

  2、state:

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

const state = store.getState()

  3、action

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

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

  4、store.dispatch():

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

store.dispatch({
    type: 'ADD_TODO'
})

 5、reducer:

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

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

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

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

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

yarn add immutable -S

使用方式:

import { Map } from 'immutable'        //引入Map函数

const user = (state = Map({        //使用Map深拷贝了state
  isLogin: localStorage.getItem('isLogin') === 'true',    
  token: localStorage.getItem('token') || '',
  adminname: localStorage.getItem('adminname') || '',
  role: localStorage.getItem('role') * 1 || 1
}), action) => {
  switch (action.type) {
    case 'CHANGE_LOGIN_STATE':
      return state.set('isLogin', action.payload)    //set方式写入
    case 'CHANGE_TOKEN':
      return state.set('token', action.payload)
    case 'CHANGE_ADMIN_NAME':
      return state.set('adminname', action.payload)
    case 'CHANGE_ROLE':
      return state.set('role', action.payload)
    default:
      return state
  }
}

export default user
state => {
    return {
        adminname: state.getIn(['user', 'adminname']),    // get方式获取值, 参数是这种形式
            //第一个参数的含义: user状态管理器中的
            //第二个参数含义: 名为adminname的状态/属性
    }
}

6、设计状态管理器

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

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

帮助解读阶段

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

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

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

// 可以通过provider和connect在组件中对状态管理器进行‘链接',链接成功之后就可以使用状态管理器中的状态和方法了
 
// /src/xxx/index.jsx
import {connect} from 'react-redux'
 
function App (props) {
    ...
}
export default connet(mapStateToProps, mapDispatchToProps)(App)
     
// /index.js
import {Provider} from 'react-redux'
import App from './App.jsx'
import store './store/index.js'
     
ReactDom.render (
    <React.StrictMode>
        <Provider store = { store }>
            <App />
        </Provider>
    </React.StrickMode>
)
 
//也可以使用到装饰器的高阶函数 @connect @withRouter
 
//以往从状态树取出对应的数据,让后通过props传给组件使用通过react-redux自带的connect()方法
class Home extends React.Component {
    //....
}
export default connect(state => ({todos: state.todos}))(Home);
 
//使用装饰器的话就变成这样,好像没那么复杂
@connect(state => ({ todos: state.todos }))
class Home extends React.Component {
    //....
}

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

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

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

mapStateToProps:

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

mapDispatchToProps:

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

const App = (props) => {
    // 组件中直接就可以通过props访问到状态管理器中的状态
    props.adminname
    props.count
    props.bannerList
    props.reduceFn
    ...
}
export default connect(
    // 可以看到这里就是传入两个函数,返回两个函数
    state => {
        return {
            adminname: state.getIn(['user', 'adminname']),  //这是一种存储状态的方式,一会会讲到
            count: state.app.count,     //参数是state,我们把app状态管理器中的count属性传递到props中的count
            bannerList: state.pro.bannerList,
        }
    },
    dispatch => {
        return {
            reduceFn () {   //我们在这里定义了一个reduceFn,里面是dispatch的方法,我们在props中就可以通过reduceFn这个方法发送'REDUCE'提交的信息
                dispatch({
                    type: 'REDUCE',
                    payload: 5  //payload为参数,可以不传
                })
            }
        }
    }
)(App)

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

redux-thunk、redux-saga

redux-thunk的使用

//在store.js之中把thunk引入并挂载到状态管理器中
 
import { createStore, combineReducers, applyMiddleware} from 'redux'
 
import thunk from 'redux-thunk'
import app from './modules/app'
import pro from './modules/pro'
 
const reducer = combineReducers({
  app, pro
})
// 通过applyMiddleware将thunk挂载到状态管理器
const store = createStore(reducer, applyMiddleware(thunk))
 
export default store

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

// .../store/actionCreator/pro.js
 
// 这个文件就是专门用来触发异步操作
// thunk模块执行的时候, actionCreator 函数有默认的参数为dispatch
// 该dispatch 可以用来触发reducer
// 有时候在触发异步的时候, 需要传递参数,这个时候,可以在函数内部返回一个 actionCreator 函数
const actions = {
  getBannerListAction (dispatch) {
    fetch('http://121.89.205.189/api/banner/list')
    .then(res => res.json())
    .then(res => {
      dispatch({
        type: 'CHANGE_BANNER_LIST',
        payload: res.data
      })
    })
  },
  getProListAction (count) {   //有参数,返回一个函数,函数参数默认为dispatch
    count = count || 1
    return function (dispatch) {
      fetch('http://121.89.205.189/api/pro/list?count='   count)
      .then(res => res.json())
      .then(res => {
        dispatch({
          type: 'CHANGE_PRO_LIST',
          payload: res.data
        })
      })
    }
  }
}
 
export default actions

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

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

import actions from './store/actionCreator/pro'
const App = (props) => {
    // props之中可以直接访问到
    props.reduceFn()
    props.addFn()
    props.getBannerList()
    props.getProList()
}
 
const mapStateToProps = (state) => {
  return {
    count: state.app.count,
    bannerList: state.pro.bannerList,
    proList: state.pro.proList
  }
}
 
const mapDispatchToProps = (dispatch) => {
  return {
    reduceFn () {       //通过正常方式修改状态
      dispatch({       
        type: 'REDUCE',
        payload: 5
      })
    },
    addFn () {
      dispatch({
        type: 'ADD',
        payload: 5
      })
    },
    getBannerList () {      //通过thunk方式修改状态
      dispatch(actions.getBannerListAction)
    },
    getProList () {
      dispatch(actions.getProListAction(2))
    }
  }
}
 
export default connect(mapStateToProps, mapDispatchToProps)(App)

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

react-saga的使用

安装redux-saga

yarn add redux-saga  immutable redux-immutable -S

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

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

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

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

import { call, put, takeLatest } from 'redux-saga/effects'
 
import { getBannerList, getProList } from '../api/home'
 
//  redux-saga    --->    必须与generator函数一起使用
 
function * getBannerListAction() {
  const res = yield call(getBannerList) //call--调用函数
  yield put({
    type: 'CHANGE_BANNER_LIST',
    payload: res.data
  })
}
function * getProListAction (action){
  const res = yield call(getProList, action.payload)
  yield put({
    type: 'CHANGE_PRO_LIST',
    payload: res.data
  })
}
 
function * mySaga () {
  yield takeLatest('REQUEST_BANNER_LIST', getBannerListAction)
  yield takeLatest('REQUEST_PRO_LIST', getProListAction)
}
 
export default mySaga

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

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

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

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

到此这篇关于对于React各种状态管理器的解读的文章就介绍到这了,更多相关React状态管理器内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

React各种状态管理器的解读及使用方法的更多相关文章

  1. ios – React native链接到另一个应用程序

    如果是错误的,有人知道如何调用正确的吗?

  2. ios – React Native – 在异步操作后导航

    我正在使用ReactNative和Redux开发移动应用程序,我正面临着软件设计问题.我想调用RESTAPI进行登录,如果该操作成功,则导航到主视图.我正在使用redux和thunk所以我已经实现了异步操作,所以我的主要疑问是:我应该把逻辑导航到主视图?我可以直接从动作访问导航器对象并在那里执行导航吗?.我对组件中的逻辑没有信心.似乎不是一个好习惯.有没有其他方法可以做到这一点?

  3. 在ios中使用带有React Native(0.43.4)的cocoapods的正确方法是什么?

    我已经挖掘了很多帖子试图使用cocoapods为本地ios库设置一个反应原生项目,但我不可避免地在#import中找到了丢失文件的错误.我的AppDelegate.m文件中的语句.什么是使用反应原生的可可豆荚的正确方法?在这篇文章发表时,我目前的RN版本是0.43.4,而我正在使用Xcode8.2.1.这是我的过程,好奇我可能会出错:1)

  4. ios – React Native WebView滚动行为无法按预期工作

    如何确保滚动事件的行为与ReactNative应用程序中的浏览器相同?

  5. ios – React Native – BVLinearGradient – 找不到’React/RCTViewManager.h’文件

    谢谢.解决方法几天前我遇到了完全相同的问题.问题是在构建应用程序时React尚未链接.试试这个:转到Product=>Scheme=>管理方案…=>点击你的应用程序Scheme,然后点击Edit=>转到Build选项卡=>取消选中ParallelizeBuild然后点击标志添加目标=>搜索React,选择第一个名为React的目标,然后单击Add然后在目标列表中选择React并将其向上拖动到该列表中的第一个.然后转到Product=>再次清理并构建项目.这应该有所帮助.

  6. ios – React Native – NSNumber无法转换为NSString

    解决方法在你的fontWeight()函数中也许变成:

  7. ios – React native error – react-native-xcode.sh:line 45:react-native:command not found命令/ bin/sh失败,退出代码127

    尝试构建任何(新的或旧的)项目时出现此错误.我的节点是版本4.2.1,react-native是版本0.1.7.我看过其他有相同问题的人,所以我已经更新了本机的最新版本,但是我仍然无法通过xcode构建任何项目.解决方法要解决此问题,请使用以下步骤:>使用节点版本v4.2.1>cd进入[你的应用]/node_modules/react-native/packager>$sh./packager.s

  8. 反应原生 – 如何通过Xcode构建React Native iOS应用程序到设备?

    我试图将AwesomeProject应用程序构建到设备上.构建成功并启动屏幕显示,但后来我看到一个红色的“无法连接到开发服务器”屏幕.它表示“确保节点服务器正在运行–从Reactroot运行”npmstart“.看起来节点服务器已经运行,因为当我做npm启动时,我收到一个EADDRINUSE消息,表示该端口已经在使用.解决方法从设备访问开发服务器您可以使用开发服务器快速迭代设备.要做到这一点,你的

  9. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

  10. 如何为iOS的React Native设置分析

    所以我已经完成了一个针对iOS的ReactNative项目,但是我想在其中分析.我尝试了react-native-google-analytics软件包,但是问题阻止了它的正常工作.此外,react-native-cordova-plugin软件包只适用于Android,因此插入Cordova插件进行分析的能力现在已成为问题.我也没有Swift/ObjectiveC的经验,所以将完全失去GA的插入.有没有人有任何建议如何连接GoogleAnalytics的ReactNativeforiOS?

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部