(1)redux-soga
saga本质就是利用generator的能把异步操作模仿为同步操作的特性
把需要的副作用代码都抽离出来写到单独的文件里去
具体流程:组件触发dispatch派发action,经过监听saga里的takeEvery拦截
符合条件的action会被阻塞,调用工作saga完成异步操作,例如网络请求数据
请求成功后调用action生成器封装成新的action发送给reducer
处理之后再传回组件,重新渲染
saga相对于thunk,对副作用代码进行了更彻底的抽离和管理
当有新业务时只需添加saga文件即可
比thunk更能适合业务逻辑繁多的大型项目
(2)获取用户信息案例
//index.js
import ReactDOM from 'react-dom';
import App from './App';
import {Provider} from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
//App.js
import {actionIncrement,actionIncrementAsync} from './store/actions/count'
import {actionUserAsync} from './store/actions/user'
import {connect} from 'react-redux'
function App({count,actionIncrement,actionIncrementAsync,actionUserAsync,user}) {
return (
<div>
<p>count:{count}</p>
<p>user:{user.name}</p>
<button onClick={actionIncrement}>Sync+</button>
<button onClick={actionIncrementAsync}>Async+</button>
<button onClick={actionUserAsync}>UserAsync</button>
</div>
);
}
const mapState = (state)=>{
return{
count:state.countReducer.count,
user:state.userReducer.user,
error:state.userReducer.error
}
}
//当UI组件中的函数名字和action生成器中的名字一样的时候可以简写
//版本1
// const mapDispatch = (dispatch) =>{
// return{
// actionIncrement:()=>{
// dispatch(actionIncrement())
// }
// }
// }
//版本1可以省略return写成版本2
//版本2
// const mapDispatch = {
// actionIncrement:()=>(actionIncrement())
// }
//版本3
// const mapDispatch = {
// actionIncrement:actionIncrement
// }
const mapDispatch = {
actionIncrement,
actionIncrementAsync,
actionUserAsync
}
//甚至可以不写mapDispatch直接导出
// export default connect(mapState,{actionIncrement})(App);
export default connect(mapState,mapDispatch)(App);
//store->index.js
import reducer from "./reducers/index.js";
import {createStore,applyMiddleware} from 'redux'
import createSagaMiddleware from 'redux-saga'
import { rootSaga } from "./sagas/index.js";
//createSagaMiddleware相当于工厂函数,生成saga中间件
const sagaMiddleware = createSagaMiddleware()
//store和sagaMiddleware连接
const store = createStore(reducer,applyMiddleware(sagaMiddleware));
//sagaMiddleware和sagas模块连接,启动一个监听saga任务
sagaMiddleware.run(rootSaga)
export default store;
//store->user.js
import * as Type from '../actionTypes'
export const actionUserAsync =() =>{
return {type:Type.USER_ASYNC}
}
export const actionUser =(user) =>{
return {type:Type.USER_OK,payload:user}
}
//store->error.js
import * as Type from '../actionTypes'
export const actionError =(msg) =>{
return {type:Type.ERROR,payload:msg}
}
//userReducer.js
import * as Type from '../actionTypes'
const defaultState = {user:0,error:''}
//第一次此使用默认值,之后使用传递过来的值
const userReducer = (state = defaultState, action) => {
switch (action.type) {
case Type.USER_OK:
let user = action.payload
return{...state,user}
case Type.ERROR:
let msg = action.payload
return { ...state,error:msg};
default:
return state
}
}
export default userReducer
//sagas->user.js
import axios from 'axios'
import { call, fork, takeEvery, all, delay, put } from 'redux-saga/effects'
import { USER_ASYNC } from '../actionTypes'
//调用action生成器
import { actionUser } from '../actions/user'
import {actionError} from '../actions/error'
//工作saga
function* getUserAsync() {
try {
let result = yield call(axios.get, 'https://jsonplaceholder.typicode.com/users/1')
let user = result.data
yield put(actionUser(user))
} catch (error) {
console.log(error)
yield put(actionError(error.msg))
}
}
//监听saga
export function* watchUserSaga() {
console.log('watchUserSaga');
yield takeEvery(USER_ASYNC, getUserAsync)
console.log('call');
}
//sagas->index.js
import { all } from 'redux-saga/effects'
import { watchUserSaga } from './user'
import { watchCountSaga} from './count'
//saga入口文件,集中触发监听sago
export function *rootSaga(){
//要使他们并发执行
yield all([
watchCountSaga(),
watchUserSaga()
])
}
注意在上个代码块中的映射的简写方法
当UI组件中的函数名字和action生成器中的名字一样的时候可以简写
版本1
const mapDispatch = (dispatch) =>{
return{
actionIncrement:()=>{
dispatch(actionIncrement())
}
}
}
版本1可以省略return写成版本2
版本2
const mapDispatch = {
actionIncrement:()=>(actionIncrement())
}
版本3
const mapDispatch = {
actionIncrement:actionIncrement
}
const mapDispatch = {
actionIncrement
}
甚至可以不写mapDispatch直接导出
export default connect(mapState,{actionIncrement})(App);
(3)soga内置的API
saga中有工作saga和监听saga
监听saga调用工作saga完成异步操作
模拟delay
const delay =(ms)=>new Promise(resolve=>setTimeout(resolve,ms))
工作saga
function* incrementAsync(){
saga提供的dalay方法
yield delay(2000)
saga提供的call不能调用saga提供的delay,只能手写
yield call(delay,2000)
put是saga给我们提供的跟dispatch功能一样的方法
等待异步操作完成后再此调用action生成器
利用put派发的新的action给reducer
yield put(actionIncrement())
}
监听saga,不会马上执行
export function* watchCountSaga(){
takeEvery拦截所有符合要求的action,若不符合则直接放过,action会直接到reducer里
takeEvery会处理每一次请求,即使是连续快速的;若连续点击三下,那就会连续增加3
yield takeEvery(Type.INCREMENT_ASYNC,incrementAsync)
takeLatest只会处理连续请求的最后一次任务
yield takeLatest(Type.INCREMENT_ASYNC,incrementAsync)
take会阻塞action直到遇到能匹配的
yield take(Type.INCREMENT_ASYNC)
fork不会阻塞
yield fork(incrementAsync)
}
//工作saga
function* getUserAsync() {
整体请求时间等于 5秒+第一次请求时间+第二次请求时间+第三次请求时间
yield delay (5000)
console.log('getUserAsync');
yield可以模拟同步,会让同步代码等待异步操作完毕后再执行
var user = yield axios.get('https://jsonplaceholder.typicode.com/users/1')
var user = yield call(axios.get,'https://jsonplaceholder.typicode.com/users/1')
console.log(user.data);
var post = yield axios.get('https://jsonplaceholder.typicode.com/posts/1')
onsole.log(post.data);
var todo = yield axios.get('https://jsonplaceholder.typicode.com/todos/1')
console.log(todo.data);
all能实现并发请求,并发请求整体时间取最慢请求的时间
const result = yield all([
delay(2000),
call(axios.get,'https://jsonplaceholder.typicode.com/users/1'),
call(axios.get,'https://jsonplaceholder.typicode.com/posts/1'),
call(axios.get,'https://jsonplaceholder.typicode.com/todos/1')
])
console.log(result);
}
//监听saga
export function* watchUserSaga() {
console.log('watchUserSaga');
yield takeEvery(USER_ASYNC, getUserAsync)
fork是非阻塞的,所以输出语句在getUserAsync还没执行完时就输出了
yield fork(getUserAsync)
console.log('fork');
call是阻塞的,所以输出语句会在getUserAsync执行完成后再输出
yield call(getUserAsync)
console.log('call');
}
(4)集中管理saga
当saga很多是我们要统一管理,为所有的saga写一个入口文件
//sagas->index.js
import { all } from 'redux-saga/effects'
import { watchUserSaga } from './user'
import { watchCountSaga} from './count'
//saga入口文件,集中触发监听sago
export function *rootSaga(){
//要使他们并发执行
yield all([
watchCountSaga(),
watchUserSaga()
])
}
|