redux redux-saga实现异步方法

基本逻辑:在组件中提交的action的type,对应在saga中要处理的type,saga中处理完后,再put提交对应在reducer中的type
使用环境:redux+react-redux+saga
	saga可以处理同步和异步
	saga就是内部yield调用了take等监听的Generator函数||yield调用了其他内部有take等监听的Generator函数	
	
1、安装
	cnpm install redux-saga --save
	cnpm install redux-saga-routines --save
	
2、引入,在创建store的文件中引入
	import {createStore,applyMiddleware} from 'redux';
	import createSagaMiddleware from 'redux-saga'

3、创建中间件
	const sagaMiddleware = createSagaMiddleware()

4、使用中间件
	const store = createStore(
	  reducer,
	  applyMiddleware(sagaMiddleware)
	)

5、单独创建一个saga文件来处理异步请求,内部使用es6的Generator函数然后导出

	在组件中提交的action的type,对应在saga中要处理的type,saga中处理完后,再put提交对应在reducer中的type
	
	import { call, put, takeEvery, takeLatest,all } from 'redux-saga/effects'
	import {x,xx} from './action-type'
	import {x1,xx1} from './actions'  对应reducer中的action,相当于使用redux-thunk时的同步action
    
    yield对应辅助函数
	    (1)监听触发
	    	 takeEvery:捕捉每一个action的类型,然后执行回调方法,支持并发同时处理多个相同action
			 takeLatest :不支持并发,如果之前已经有一个action在处理中,再执行一个相同的action,前面的action会被取消
			 take:和takeEvery不同的是,只会调用一次,需要循环等才能实时监听
			 	(1)用法:const action = yield take(匹配的type); action为匹配的action
			 		yield take(匹配的type);
			 		yield take([匹配的type1,匹配的type2]);
			 	(2)while(true)中使用,每次会阻塞等待action发起
			 	(3)可以对每次的action做人为调控,限制次数后取消监听等
			 all:[内部调用了诸如yield takeEvery的监听函数],用于统一监听
		 
		(2)提交
		 	put:相当于dispatch
		 
		(3)异步方法调用
			 call:用来调用函数,表示同步做异步的事情
			 	yield call(函数,参数1,参数2,..)
			 	yield call([对象,对象.方法],参数)
			 apply:用来调用函数,表示同步做异步的事情
			 	yield apply(对象,对象.方法,[参数])
			 cps: 用来处理Node风格的函数fn(...args, callback) 中的 callback 是 (error, result) => () 这样的形式,cps 表示的是延续传递风格(Continuation Passing Style))。
			 	yield cps(fn,参数);
			 fork:不会阻塞进程,yield call等在调用结束前会阻塞进程,调用方式和call相同
		 		(1)const task = yield fork(fn,参数)
		 		(2)yield cancle(task);  取消fork任务,所有深层等待的操作都将被取消。
		 		(3)如果调用了yield cancle,在fork对应的fn中,可通过在finally区通过if(yield cancelled()),来对取消操作进行逻辑判断
		 			try{...}catch(err){...}finally{ if(yield cancelled()) }
		 		
		 	 同时执行多个异步任务
		 		const [x,x2]=yield [call(fn,参数),apply(对象,对象.fn,[参数])]
		 			当yield一个包含effects的数组,generator 会被阻塞直到所有的effects都执行完毕,或者当一个effect被拒绝(就像Promise.all的行为)。
		 			
		(4)查询状态
			 const state = yield select();
			 	返回当前reducer的状态
			 	
		(5)获取第一个任务race
			const {x,xx}=yield race({
				x:yield call(...),
				xx:yield take(...)
			})
			只会获取第一个执行完成的任务结果,并取消其他所有未完成的effect
			
		(6)对saga进行排序,只有触发了前一个saga才会触发后面的
			saga就是内部调用了take等监听的Generator函数	
			yield* fn1
			yield* fn2

	(1)创建匹配type后的处理Generator
		function* 回调方法(action)
		{
		    action:匹配到的type对应的acion内容
			异步操作
			try{
				const res=yield 异步方法;
				yield put(x1(res.data));   通过派发action更新数据
				
			}catch(e)
			{
				获取数据失败的情况
			}
			
		}
	(2)创建用来监听type的Generator即saga,以及设置监听对应的处理Generator
		function* xxx()
		{
			yield takeEvery(x,回调方法);
				当action的type为x时,且被dispatch执行后,会调用回调方法
		}
	
		export defaut xxx

	(3)若管理多个监听Generator,即统一管理saga
	
		(1)export default function* rootSaga() {  将该函数在store.js中run
		   yield all([
		      xxx(),
		      xxx2()
		    ])
		  }
		  
	   	(2)export default function* rootSaga() {  将该函数在store.js中run
		    yield takeEvery(type,回调方法1);
		    yield takeEvery(type,回调方法2);
		  }
	  
		


6、运行导出的saga中的监听type的函数,在创建的store文件中
	import xx from './x'
	
	sagaMiddleware.run(xx)

7、在action-type.js和actions.js中写对应saga的相应代码

	对应的type类型是saga用于接收的type,不是reducer,在saga中调用reducer的action来改变状态
	actions.js中返回正常对象即可
		{type:xxx,data:xxx}

8、组件中派发用于saga的action
	import {y,yy} from '../redux/actions'
	
	props.y或store.dispatch.yy后,reducer和saga文件都会接收这个action

代码示例:
saga.js

import { call, put, takeEvery, takeLatest,all,delay } from 'redux-saga/effects'
import {decrement} from '../actions'
import axios from 'axios'

async function http()
{
    let res=await axios.get('http://api.tianapi.com/txapi/ncov/index?key=4a8edfc8ac5eae9b0c5bf08157abba96')
    return res.data;
}

function* incrementAsync() {
    let res= yield call(http);
    console.log(res);
    yield delay(2000);
    yield put({type:'INCREMENT'})
}

function* decrementAsync() {
    yield delay(2000);
    yield put(decrement())
}


function* watchIncrementAsync() {
    yield takeEvery('INCREMENT_ASYNC', incrementAsync)
}

function* watchDecrementAsync() {
    yield takeEvery('DECREMENT_ASYNC', decrementAsync)
}

export default function* rootSaga() {
    yield all([
      watchIncrementAsync(),
      watchDecrementAsync()
    ])
    // yield takeEvery('INCREMENT_ASYNC', incrementAsync)
    // yield takeEvery('DECREMENT_ASYNC', decrementAsync)

  }

actions.js:

export const increment=()=>({type:'INCREMENT'})
export const decrement=()=>({type:'DECREMENT'})

export const increment_async=()=>({type:'INCREMENT_ASYNC'})
export const decrement_async=()=>({type:'DECREMENT_ASYNC'})

reducers.js:


const init={
    count:0
}

export function counter(state=init,action){
    switch(action.type)
    {
        case 'INCREMENT':
            return {count:state.count+1}
        case 'DECREMENT':
            return {count:state.count-1}

        default:
            return state;
    }
}


store.js:

import {createStore,combineReducers,applyMiddleware} from 'redux'
import {composeWithDevTools} from 'redux-devtools-extension'
import createSagaMiddleware from 'redux-saga'
import rootSaga from './saga/sagas'

import {counter} from './reducers'


const reducers=combineReducers({
    counter
})

console.log(counter);
const sagaMiddleware = createSagaMiddleware()

const store=createStore(reducers,composeWithDevTools(applyMiddleware(sagaMiddleware)))

sagaMiddleware.run(rootSaga);

console.log(store);

export default store;



App.js:

import React from 'react';
import './App.css';

import {adminRouter} from './routes'
import {Route,Switch,Redirect} from 'react-router-dom'
import {connect} from 'react-redux'

import {increment,decrement,increment_async,decrement_async} from './store/actions'

class App extends React.Component {
  onIncrementAsync=()=>{
    this.props.increment_async()
  }
  onDecrementAsync=()=>{
    this.props.decrement_async();
  }
  onIncrement=()=>{
    this.props.increment();
  }
  onDecrement=()=>{
    this.props.decrement();
  }
  componentDidMount()
  {
    console.log(this.props);
  }
  render()
  {
    return (
      <div className="App">
            <button onClick={this.onIncrementAsync}>
              Increment after 1 second
            </button>
            {' '}
            <button onClick={this.onDecrementAsync}>
              decrement after 1 second
            </button>
            {' '}
            <button onClick={this.onIncrement}>
              Increment
            </button>
            {' '}
            <button onClick={this.onDecrement}>
              Decrement
            </button>
            <hr />
            <div>
              Clicked: {this.props.count} times
            </div>
      </div>
    );

  }
}

export default connect((state)=>({
  count:state.counter.count
}),{increment,decrement,increment_async,decrement_async})(App);

index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import {HashRouter,Route,Switch,Redirect} from 'react-router-dom'
import {mainRouter} from './routes'
import App from './App'
import * as serviceWorker from './serviceWorker';
import {Provider} from 'react-redux'
import store from './store/store'

ReactDOM.render(
  <Provider store={store}>
    <React.Fragment>
      <App/>
    </React.Fragment>
  </Provider>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

代码示例二:

import {call, put, takeEvery,take, takeLatest,delay,all,apply,select,race} from 'redux-saga/effects';
import {SAGA_ADD,ADD_COUNT,SAGA_ADD_REDUCER} from '../action-types'
import {sagaAdd} from '../actions'

import axios from 'axios';

async function api(url)
{
    let res=await axios.get(url);
    return res;
}

function *sagaHandler(action)
{
    // console.log(action);
    console.log('www');
    // let res = yield apply(this,api,['http://api.tianapi.com/txapi/ncov/index']);
    let res=yield call(api,'http://api.tianapi.com/txapi/ncov/index')

    // yield delay(3000);
    yield put({type:SAGA_ADD,data:res});
    
}
function *watchSagaAdd()
{
    console.log('www');
    yield takeEvery(SAGA_ADD_REDUCER,sagaHandler);
    
}


function *taketest()
{
    // while(true) //会阻塞等待action发起。
    // {
        const action =yield take('*');
        
        console.log(action);
    // }
}

function *mySaga()
{
    
    yield all([watchSagaAdd(),taketest()])
    // yield watchSagaAdd();
    // yield taketest()
    
    

}

export default mySaga;


//在组件中提交的action的type,对应在saga中要处理的type,saga中处理完后,再put提交对应在reducer中的type
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值