基本逻辑:在组件中提交的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