Redux-saga 简介
- redux-saga 和 redux-thunk 一样, 是一个 Redux 中获取存储异步数据的中间件
- redux-saga 可以直接拦截 dispatch 派发的 action, 从而实现在执行 reducer 之前执行一些其它操作
使用 Redux-saga
- 安装 Redux-saga
npm install redux-saga
- 在创建 store 时应用
redux-saga
中间件,更改 store.js
import {createStore, applyMiddleware} from 'redux' /* 注意点: 如果导入的是redux-thunk, 那么返回给我们的是一个中间件对象 如果导入的是redux-saga, 那么返回给我们的是一个用于创建中间件对象的方法 * */ import createSagaMiddleware from 'redux-saga' import reducer from './reducer'; // 通过createSagaMiddleware方法创建saga中间件对象 const sagaMiddleware = createSagaMiddleware(); // 创建store之前,通过applyMiddleware方法,告诉Redux需要应用哪些中间件 const storeEnhancer = applyMiddleware(sagaMiddleware); // 利用store来保存状态(state) const store = createStore(reducer, storeEnhancer); /* 注意点: 如果是redux-thunk, 那么在创建store的时候指定完中间件即可 如果是redux-saga, 那么除了需要在创建store的时候指定中间件以外, 还需要手动的调用中间件的run方法才行 * */ sagaMiddleware.run(undefined, undefined); export default store;
- 我们可以利用传入的生成器告诉 redux-saga, 需要拦截哪些 dispatch 派发的 action,声明一下,至于什么是生成器可去查看一下博主
JS 流程框架与特性
的标签里面会进行介绍什么是生成器,然后这个陌生的问题就过,我们继续,创建 saga.js 在当中定义生成器代码,在生成器函数中获取网络数据:
import {takeEvery, put} from 'redux-saga/effects' import {GET_USER_INFO} from './constants'; import {changeAction} from './action'; function* myHandler() { // 获取网络数据 const data = yield fetch('http://127.0.0.1:7001/info') .then((response) => { return response.json(); }) .catch((error) => { console.log(error); }); // 保存获取到的数据 // 相当于 store.dispatch(changeAction()); yield put(changeAction(data)); } function* mySaga() { // 指定需要拦截的action类型 // 指定拦截到这个类型的action之后交给谁来处理 yield takeEvery(GET_USER_INFO, myHandler) } export default mySaga;
如上自定义函数已经获取到了网络数据,添加到 Redux 中保存是通过 Saga 提供的 put 方法进行添加即可,在更改 store.js 告诉 saga 中间件的生成器哪些通过 dispatch 派发的 action 需要进行拦截, 在 run 方法进行指定:
- 在组件中派发 action 这回我们派发的 action 就不用像 thunk 一样派发一个函数了
除了通过 takeEvery
来拦截派发的 action 任务方式之外还可以通过 takeLatest
进行监听。
takeEvery 和 takeLatest 区别
区别主要在于是否能够完整的执行监听方法:
- 对于 takeEvery 而言,每次拦截到对应类型的 action, 都会完整的执行监听方法
- 对于 takeLatest 而言, 每次拦截到对应类型的 action, 都不能保证一定能够完整的执行监听方法
例如: 连续派发了 3 次 GET_USER_INFO 的 action, 那么对于 takeEvery 而言, myHandler 就会被完整的执行 3 次,那么对于 takeLatest 而言, 如果派发下一次同类型 action 的时候,上一次派发的 action 还没有处理完, 也就是上一次的监听方法还没有处理完,那么 takeLatest 会放弃还没有处理完的代码, 直接开始处理下一次的 action。
那么问题来了,如果想要验证如上博主所说的这点,就必须要派发多次 action 那么该如何进行派发呢,正好可以借助该问题就可以引出一个全新的知识点了那么就是连续派发多个 action,如果我们只需要拦截一个类型的 action, 那么直接通过 yield takeEvery / yield takeLatest 即可,但是如果我们想同时拦截多个类型的 action, 那么我们就必须借助另外一个函数, all()
。
sage.js:
import {takeLatest, put, all} from 'redux-saga/effects' import {GET_USER_INFO, ADD_COUNT, SUB_COUNT} from './constants'; import {changeAction} from './action'; function* myHandler() { // 获取网络数据 const data = yield fetch('http://127.0.0.1:7001/info') .then((response) => { return response.json(); }) .catch((error) => { console.log(error); }); yield put(changeAction(data)); console.log('myHandler'); } function* mySaga() { yield all([ yield takeLatest(GET_USER_INFO, myHandler), yield takeLatest(GET_USER_INFO, myHandler), yield takeLatest(GET_USER_INFO, myHandler), ]); } export default mySaga;
About.js:
import React from 'react'; import {changeAction, getUserInfo, addAction} from "../store/action"; import connect from "../connect/connect"; import {ADD_COUNT} from "../store/constants"; class About extends React.PureComponent { componentDidMount() { this.props.getUserInfo(); } render() { return ( <div> <p>{this.props.info.name}</p> <p>{this.props.info.age}</p> </div> ) } } const mapStateToProps = (state) => { return { info: state.info, count: state.count } }; const mapDispatchToProps = (dispatch) => { return { changeInfo() { dispatch(changeAction()); }, getUserInfo(){ dispatch(getUserInfo()) }, addCount(){ dispatch(addAction(1)) } } }; export default connect(mapStateToProps, mapDispatchToProps)(About);
关于如上博主举的 takeLatest 栗子没有体现出来,不知道是否是本地体现不了该场景,如有大佬发现问题,还请在下方评论区指出,共同学习。(博主所说的是:如果派发下一次同类型 action 的时候,上一次派发的 action 还没有处理完, 也就是上一次的监听方法还没有处理完)
如果我们只需要保存一个数据, 那么直接通过 yield put 即可,但是如果我们想同时保存多个数据 , 那么我们就必须借助另外一个函数, all()
:
saga.js:
import {takeLatest, put, all} from 'redux-saga/effects' import {GET_USER_INFO, ADD_COUNT, SUB_COUNT} from './constants'; import {changeAction} from './action'; function* myHandler() { // 获取网络数据 const data1 = yield fetch('http://127.0.0.1:7001/info') .then((response) => { return response.json(); }) .catch((error) => { console.log(error); }); const data2 = yield fetch('http://127.0.0.1:7001/info') .then((response) => { return response.json(); }) .catch((error) => { console.log(error); }); yield all([ yield put(changeAction(data1)), yield put(changeAction(data2)), yield put({type: 'CHANGE_INFO', info: {name: 'test'}}), yield put({type: 'CHANGE_USER_Age', age: data1.age}), ]) } function* mySaga() { yield all([ yield takeLatest(GET_USER_INFO, myHandler), yield takeLatest(ADD_COUNT, myHandler), yield takeLatest(SUB_COUNT, myHandler), ]); } export default mySaga;
About.js:
import React from 'react'; import {changeAction, getUserInfo} from "../store/action"; import connect from "../connect/connect"; class About extends React.PureComponent { componentDidMount() { this.props.changeInfo(); this.props.getUserInfo(); } render() { return ( <div> <p>{this.props.info.name}</p> <p>{this.props.info.age}</p> </div> ) } } const mapStateToProps = (state) => { return { info: state.info } }; const mapDispatchToProps = (dispatch) => { return { changeInfo() { dispatch(changeAction()); }, getUserInfo(){ dispatch(getUserInfo()) } } }; export default connect(mapStateToProps, mapDispatchToProps)(About);
官方文档地址:https://redux-saga-in-chinese.js.org/
最后
本期结束咱们下次再见👋~
🌊 关注我不迷路,如果本篇文章对你有所帮助,或者你有什么疑问,欢迎在评论区留言,我一般看到都会回复的。大家点赞支持一下哟~ 💗