(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()
  ])
}

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐