1.Redux-saga
- redux-saga和redux-thunk一样,是一个Redux中获取存储异步数据的中间件
- redux-saga可以直接拦截dispatch派发的action,从而实现在执行reducer之前执行一些其他操作
2.如何使用Redux-saga
安装
$ npm install redux-saga
使用
store/index.js
import { createStore, applyMiddleware } from "redux"
import reducer from "./reducer"
import MySaga from "./saga"
/**
* redux-thunk
import thunkMiddleware from "redux-thunk"
// 创建store之前,通过applyMiddleware方法,告诉Redux需要引用哪些中间件
const storeEnhancer = applyMiddleware(thunkMiddleware)
// 利用store来保存状态(state)
const store = createStore(reducer, storeEnhancer)
*/
/**
* 注意点:
* 如果导入的是redux-thunk,那么返回的是一个中间件对象
* 如果导入的是redux-saga,那么返回的是一个用于创建中间件对象的方法
*/
import createSagaMiddleware from 'redux-saga'
// 通过createSagaMiddleware方法创建saga中间件对象
const sagaMiddleware = createSagaMiddleware()
// 创建store之前,通过applyMiddleware方法,告诉Redux需要应用哪些中间件
const storeEnhancer = applyMiddleware(sagaMiddleware)
const store = createStore(reducer, storeEnhancer)
/**
* 注意点:
* 如果是redux-thunk, 那么在创建store的时候指定完中间件即可
如果是redux-saga, 那么除了需要在创建store的时候指定中间件以外, 还需要手动的调用中间件的run方法才行
*/
// 我们可以利用传入的生成器告诉redux-saga, 需要拦截哪些dispatch派发的action
sagaMiddleware.run(MySaga)
export default store
action
import { ADD_COUNT, CHANGE_ACTION, GET_USER_INFO } from "./constants"
export const addAction = (num) => {
console.log('num', num)
return {
type: ADD_COUNT,
num
}
}
export const changeAction = (info) => {
return {
type: CHANGE_ACTION,
info
}
}
export const getInfo = (dispatch, getState) => {
fetch('http://127.0.0.1:7001/info')
.then((res) => {
return res.json()
})
.then((data) => {
dispatch(changeAction(data))
})
.catch(error => {
console.log('error', error)
})
}
export const getUserInfo = () => {
return {
type: GET_USER_INFO
}
}
constant.js
export const ADD_COUNT = 'ADD_COUNT'
export const CHANGE_ACTION = 'CHANGE_ACTION'
export const GET_USER_INFO = 'GET_UER_INFO'
reducer.js
import { ADD_COUNT, CHANGE_ACTION } from "./constants"
const initialState = {
count: 1,
info: {}
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case ADD_COUNT:
return { ...state, count: state.count + action.num }
case CHANGE_ACTION:
return { ...state, info: action.info }
default:
return state
}
}
export default reducer
saga.js
import { put, takeEvery } 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((res) => {
return res.json()
})
.catch(error => {
console.log('error', error)
})
// 保存获取到的数据
// 相当于 store.dispatch(changeAction());
yield put(changeAction(data))
}
function* MySaga() {
// GET_USER_INFO:指定需要拦截的action类型
// MyHandler:指定拦截到这个类型的action之后交给谁来处理
yield takeEvery(GET_USER_INFO, MyHandler)
}
export default MySaga
connect.js
import React from "react"
import store from '../index'
function connect(mapStateToProp, mapStateToAction) {
return function enhanceComponent(WrapComponent) {
class AdvComponent extends React.PureComponent {
constructor (props) {
super(props)
console.log('this/props', this.props)
this.state = {
...mapStateToProp(store.getState())
}
}
componentDidMount() {
store.subscribe(() => {
this.setState({
...mapStateToProp(store.getState())
})
})
}
render() {
return (
<WrapComponent {...this.props} {...this.state} {...mapStateToAction(store.dispatch)}></WrapComponent>
)
}
}
return AdvComponent
}
}
export default connect
原理:高阶组件(传入一个组件,返回一个组件)。connect将传入的【mapStateToProps: 需要将store中保存的哪些数据映射到
当前组件
【传入的组件】的props上、mapDispatchToProps: 需要将哪些派发的任务映射到当前组件
【传入的组件】的props上】,然后返回一个新的组件
调用
import React from "react"
import { connect } from "react-redux"
// import connect from "../../store/connect/connect"
import { addAction, getInfo, getUserInfo } from '../../store/action'
class About extends React.PureComponent {
componentDidMount() {
// this.props.getInfo()
this.props.changeInfo()
}
render() {
return (
<div id={'about'}>
About-----------
<div>{this.props.count}</div>
<button onClick={() => this.props.increment()}>++</button>
<div>{this.props.info.name}</div>
<div>{this.props.info.age}</div>
</div>
)
}
}
// 在mapStateToProps方法中告诉React-Redux, 需要将store中保存的哪些数据映射到当前组件的props上
const mapStateToProps = (state) => {
return {
count: state.count,
info: state.info
}
}
// 在mapDispatchToProps方法中告诉React-Redux, 需要将哪些派发的任务映射到当前组件的props上
const mapDispatchToProps = (dispatch) => {
return {
increment() {
dispatch(addAction(1))
},
getInfo() {
// redux-thunk中间件作用:
// 可以让dispatch方法可以接收一个函数, 可以让我们在通过dispatch派发任务的时候去执行我们传入的方法
dispatch(getInfo)
},
changeInfo() {
// redux-saga 正常派发,指定的类型会被拦截
dispatch(getUserInfo())
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(About)
all
拦截多个类型的action, 那么我们就必须借助另外一个函数, all()
import {takeEvery, 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 put 即可
但是如果我们想同时保存多个数据 , 那么我们就必须借助另外一个函数, all()
* */
// yield put(changeAction(data));
yield all([
yield put(changeUserAction(data1)),
yield put(changeInfoAction(data2)),
yield put({type:'CHANGE_USER_NAME', name: data1.name}),
yield put({type:'CHANGE_USER_Age', name: data1.age}),
])
console.log('执行到了监听方法的最后', data);
}
function *mySaga() {
/*
takeEvery和takeLatest区别: 是否能够完整的执行监听方法
对于takeEvery而言, 每次拦截到对应类型的action, 都会完整的执行监听方法
对于takeLatest而言, 每次拦截到对应类型的action, 都不能保证一定能够完整的执行监听方法
例如: 连续派发了3次GET_USER_INFO的action,
那么对于takeEvery而言, myHandler就会被完整的执行3次
那么对于takeLatest而言, 如果派发下一次同类型action的时候
上一次派发的action还没有处理完, 也就是上一次的监听方法还没有处理完
那么takeLatest会放弃还没有处理完的代码, 直接开始处理下一次的action
* */
// yield takeEvery(GET_USER_INFO, myHandler)
// yield takeLatest(GET_USER_INFO, myHandler)
/*
如果我们只需要拦截一个类型的action, 那么直接通过 yield takeEvery / yield takeLatest即可
但是如果我们想同时拦截多个类型的action, 那么我们就必须借助另外一个函数, all()
* */
yield all([
yield takeEvery(GET_USER_INFO, myHandler),
yield takeEvery(ADD_COUNT, myHandler),
yield takeEvery(SUB_COUNT, myHandler),
]);
}
export default mySaga;
Redux-DevTools的用法
import { createStore, applyMiddleware, compose } from "redux"
import reducer from "./reducer"
import MySaga from "./saga"
/**
* redux-thunk
import thunkMiddleware from "redux-thunk"
// 创建store之前,通过applyMiddleware方法,告诉Redux需要引用哪些中间件
const storeEnhancer = applyMiddleware(thunkMiddleware)
// 利用store来保存状态(state)
const store = createStore(reducer, storeEnhancer)
*/
/**
* 注意点:
* 如果导入的是redux-thunk,那么返回的是一个中间件对象
* 如果导入的是redux-saga,那么返回的是一个用于创建中间件对象的方法
*/
import createSagaMiddleware from 'redux-saga'
// 通过createSagaMiddleware方法创建saga中间件对象
const sagaMiddleware = createSagaMiddleware()
// 创建store之前,通过applyMiddleware方法,告诉Redux需要应用哪些中间件
const storeEnhancer = applyMiddleware(sagaMiddleware)
// 进行Redux DevTools配置
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true }) || compose
const store = createStore(reducer, composeEnhancer(storeEnhancer))
/**
* 注意点:
* 如果是redux-thunk, 那么在创建store的时候指定完中间件即可
如果是redux-saga, 那么除了需要在创建store的时候指定中间件以外, 还需要手动的调用中间件的run方法才行
*/
// 我们可以利用传入的生成器告诉redux-saga, 需要拦截哪些dispatch派发的action
sagaMiddleware.run(MySaga)
export default store
学习笔记