最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Redux-Saga基本使用

    正文概述 掘金(Tsuki_)   2021-06-19   563

    1,Redux-Saga简述:

    redux-saga是redux的中间件,主要负责处理从action派发到更新store装填中间具有副作用行为的处理。

    2,开始使用Redux-Saga

    • 下面是react的入口文件,我们使用了redux,react-redux进行状态管理,并使用了redux-saga中间件。

          // 当前路径:src/index.js
      
          // 第三方模块引入
          import React from 'react'  
          import ReactDom from 'react-dom'
          import { Provider } from 'react-redux'
          import { createStore, applyMiddleware } from 'redux'
          import createSagaMiddleware from 'redux-saga'
      
          // 自定义模块引入
          // 1,redux中的 reducer引入
          import rootReducer from './LearningSaga/reducer'
          // 2,react中 Counter组件引入
          import Counter from './LearningSaga/component'
          // 3, redux-saga中间件的 saga文件引入
          import rootSaga from './LearningSaga/saga'
      
          // 4,创建一个redux-saga中间件
          const sagaMiddleware = createSagaMiddleware()
          // 5,将redux-saga中间件加入到redux中
          const store = createStore(rootReducer, {}, applyMiddleware(sagaMiddleware))
          // 6,动态的运行saga,注意 sagaMiddleware.run(rootSaga) 只能在applyMiddleware(sagaMiddleware)之后进行
          sagaMiddleware.run(rootSaga)
      
          // 7,挂载react组件
          ReactDom.render(<Provider store={store}><Counter /></Provider>, document.getElementById('root'))
      
      
    • 注释1是我们要用到的reducer,下面是reducer文件内容,很简单,我们创建了counterReducer,并使用redux提供的combineReducers处理并返回出去,熟悉redux使用的话,这里应该不用太多赘述,就是一个简单的对state.counter进行递增处理的reducer。

      // 当前路径:src/LearningSaga/reducer/index.js
      
      import { combineReducers } from 'redux'
      
      function counterReducer(state = 1, action = {}) {
          switch (action.type) {
              case "increment": return state + 1;
              default: return state
          }
      }
      
      const rootReducer = combineReducers({ counter: counterReducer })
      
      export default rootReducer
      
    • 注释2是我们react挂载的一个Counter组件,下面是Counter组件代码,也是个很简单的组件,其中使用了react-redux对Counter进行处理,这样就能在this.props中获取到redux中的状态以及dispatch方法。

      // 当前路径:src/LearningSaga/component/index.js
      
      import React from 'react'
      import { connect } from 'react-redux'
      
      class Counter extends React.Component {
          // 派发一个type为 increment_saga 的action
          add = () => this.props.dispatch({ type: 'increment_saga' })
          render() {
              return (
                  <div>
                      <span>{this.props.counter}</span>
                      <button onClick={this.add}>add1-sync</button>
                  </div>
              )
          }
      }
      const mapStateToProps = state => ({ counter: state.counter })
      export default connect(mapStateToProps)(Counter)
      
    • 注释4567完成了对redux创建,redux中redux-saga中间件的使用,以及Counter组件的挂载等。当然这里redux-saga中间件处理方式可能和其它中间件方式略有不同。

    • 在讲注释3之前我们先回顾一下Counter组件的使用,首先Counter组件渲染出store(redux)中counter数据和一个递增按钮add1-sync,点击该按钮将派发一个类型为increment_saga的action。我们期待的事 是该动作派发后,store中的counter数据能够加1,其实如果仅是如此,我们派发一个类型为increment的action就可以做到,因为我们在注释1的reducer中已经实现了对类型为increment的action处理。但我们这里要用redux-saga中间件替我们完成,我们可以想一下redux-saga中间件要完成这个加1行为大概要做怎样一件事,首先,拦截到action,然后做一些处理,最后更新store中的counter数据完成+1。没错,redux-saga中间件的流程就是这样,所以按照这个流程回头看,这里我们派发一个类型为increment_saga的action,redux-saga中间件获取到该action,因为这个行为很纯粹就是+1,redux-saga中间件再继续调用dispatch({type:'increment'})完成对store中counter数据+1。

    • 现在回到注释3,这是redux-saga中saga文件,下面是该文件的代码,我们看到该文件中的函数全是generator函数,没错,redux-saga的世界中,就是通过这些generator函数完成dispatch过程中副作用的处理(当然我们现在这个+1例子中还没涉及到一些强烈具有副作用的行为),这些generator函数我们叫它saga。saga中yield 后面的内容我们称呼它为Effects(redux-saga的任务单元),在Effects中我们可以进行启动其它saga,也可以处理一些副作用操作。

      // 当前路径:src/LearningSaga/saga/index.js
      
      import { all, put, takeEvery } from 'redux-saga/effects'
      
      function* increment() {
          // 相当于:dispatch({ type: 'increment' })
          yield put({ type: 'increment' }) 
      }
      function* watchIncrement() {
          // 监听类型为increment_saga的action,监听到启动increment
          yield takeEvery('increment_saga', increment) 
      }
      
      function* rootSaga() {
          // 启动watchIncrement
          yield all([watchIncrement()])
      }
      export default rootSaga
      
      • 这里我们导出的是一个rootSaga函数,该函数内容很简单,只有一行 yield all([watchIncrement()]),其中all是redux-saga所提供的一个方法,用来启动一个或多个Effects,这里我们只启动了一个watchIncrement的saga。

      • 继续看watchIncrement这个saga,函数内容仅有一行yield takeEvery('increment_saga', increment),其中takeEvery是redux-saga提供的一个方法,该方法传入两个参数(type,saga),其中type即对应action中的type,而saga则是redux-saga中间件匹配到对应type的action时需要启动的saga。所以这行代码的作用很简单,就是监听action类型为increment_saga,并启动对应saga进行处理该action。 现在假设watchIncrement监听到类型为increment_saga的动作,启动increment这个saga进行处理。我们进入increment函数中看看做了什么。

      • increment这个saga中也仅有一行代码 yield put({ type: 'increment' }),这行代码中put也是redux-saga提供的一个方法,其参数为一个action,其作用是产生一个dispatch行为的Effect,其action就是put中的参数action。我们可以理解这个动作就相当于dispatch({ type: 'increment' })。所以这里将派发一个类型为increment动作去更新store中的state。

      • 现在整个saga文件已经介绍完它的作用,我们来复盘从Counter组件点击add1-sync按钮那一刻到最后store中的counter数据加1的流程。

        • 1,react入口文件中注释6启动了rootSaga,rootSaga执行yield all([watchIncrement()]),即启动了watchIncrement这个saga,并等待它运行完成

        • 2,watchIncrement的作用在前面说了,是监听类型为increment_saga的action,如果监听到,则启动increment这个saga对action进行进一步处理。

        • 3,现在Counter组件中add1-sync按钮点击,派发一个类型为increment_saga的动作。

        • 4,watchIncrement这个saga监听到该动作(type:'increment_saga'),启动increment对该action进行处理。

        • 5,increment中通过 yield put({ type: 'increment' }) 派发一个类型为increment的action出去

        • 6,reducer接受到类型为increment的action,执行对应的更新行为,完成store中counter数据+1的过程。

        • 7,最后更新Counter组件中的this.props.counter数据(这个自动更新行为由react-redux替我们完成)。

    3,带有副作用的counter数据更新

    • 在原Counter组件上新增add1-async按钮

      // 当前路径:src/LearningSaga/component/index.js
      
      import React from 'react'
      import { connect } from 'react-redux'
      
      class Counter extends React.Component {
          add = () => this.props.dispatch({ type: 'increment_saga' })
          // addAsync函数将派发一个类型为incrementAsync_saga的action
          addAsync = () => this.props.dispatch({ type: 'incrementAsync_saga' })
          render() {
              return (
                  <div>
                      <span>{this.props.counter}</span>
                      <button onClick={this.add}>add1-sync</button>
                      <button onClick={this.addAsync}>add1-async</button>
                  </div>
              )
          }
      }
      const mapStateToProps = state => ({ counter: state.counter })
      export default connect(mapStateToProps)(Counter)
      
    • 我们也要在saga文件中添加incrementAsync_saga动作对应的saga进行处理

      // 当前路径:src/LearningSaga/saga/index.js
      
      import { all, put, takeEvery, delay } from 'redux-saga/effects'
      
      function* increment() {
          yield put({ type: 'increment' }) // 相当于:dispatch({ type: 'increment' })
      }
      function* incrementAsync() {
          // 延迟1s
          yield delay(1000)
          // 1s后,dispatch({ type: 'increment' })
          yield put({ type: 'increment' })
      }
      function* watchIncrement() {
          yield takeEvery('increment_saga', increment) // 监听类型为increment_saga的action,监听到启动increment
      
          // 监听类型为incrementAsync_saga的action,监听到启动incrementAsync
          yield takeEvery('incrementAsync_saga', incrementAsync)
      }
      function* rootSaga() {
          yield all([watchIncrement()]) // 启动watchIncrement
      }
      
      export default rootSaga
      
      • 我们在watchIncrement添加了新的一行代码 yield takeEvery('incrementAsync_saga', incrementAsync),上一节中学习我们知道,这行代码作用是 监听类型为incrementAsync_saga的action,监听到启动incrementAsync。

      • 继续看incrementAsync,相较于之前的increment,多了一行代码 yield delay(1000),其中delay是redux-saga提供的延迟函数,该行代码表示延迟1s后才可以继续处理之后的代码。其本质可以看做这么个函数。

        // delay 相当于这里的 delay_
        function delay_(timeout) {
            return new Promise(r => {
                setTimeout(() => r(), timeout);
            })
        }
        
      • 所以延迟1s后,继续执行 yield put({ type: 'increment' }),即相当于dispatch({ type: 'increment' }) 完成counter更新。


    起源地下载网 » Redux-Saga基本使用

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元