Redux-saga快速解读

一、概览

首先,对于redux来说,redux-saga就是一个中间件。

其次,redux-saga主要用于处理action中涉及到的一些副作用,来确保reducer始终都是一个纯函数。

常规操作redux中的state,流程是这样的:

UI(点击按钮) → dispatch(action)→ reducer(根据action操作state)→ store(合并reducer更新的state)→ UI更新

toLoginIn: (username, password)=>{
  dispatch({
    type: 'TO_LOGIN_IN',
    username,
    password
  })
}

我们需要根据action发送一个请求,dispatch依旧是正常封装action,那么发送请求的操作就需要交给reducer来实现,可是这就违背了reducer应该是一个纯函数的设计初衷。

我们可以将带有副作用的操作先交给saga来完成,然后再由saga发出一个action,加入了saga,流程就变成了这样:

UI(点击按钮) → dispatch(action)→ take(拦截action)→ call(发送请求)→ put(根据结果转发对应的action2)→ reducer(根据action2操作state)→ store(合并reducer更新的state)→ UI更新

while(true){
  const action = yield take('TO_LOGIN_IN'); // 在发出type为TO_LOGIN_IN的action时,截获该action
  const res = yield call(fetchSmart, '/login', {
    method: 'POST',
    body: JSON.stringify({
      username: action.username,
      password: action.password
    })
    if(res){
      put({type: 'to_login_in'})
    }
  })
}

可以看到,put的作用就很像是dispatch。 

发送请求的部分最好还是使用fork,它不会像call那样阻塞线程,影响后续代码的执行。

我们来看一下“点击按钮登录 → 请求验证 → 请求资源 → 退出登录” 的完整代码

function * getList(){
  try {
    yield delay(3000);
    const res = yield call(fetchSmart,'/list',{
      method:'POST',
      body:JSON.stringify({})
    });
    yield put({type:'update_list',list:res.data.activityList});
  } catch(error) {
    yield put({type:'update_list_error', error});
  }
}

function * watchIsLogin(){
  while(true){
    //监听登入事件
    const action1=yield take('TO_LOGIN_IN');
    
    const res=yield call(fetchSmart,'/login',{
      method:'POST',
      body:JSON.stringify({
        username:action1.username,
        password:action1.password
      })
    });
    
    //根据返回的状态码判断登陆是否成功
    if(res.status===10000){
     yield put({type:'to_login_in'});
      //登陆成功后获取首页的活动列表
      yield call(getList);
    }
    
    //监听登出事件
    const action2=yield take('TO_LOGIN_OUT');
    yield put({type:'to_login_out'});
  }
}

可以看到,我们使用yield call(getList)获取的资源,假如网络较慢,线程就一直会处于阻塞状态,这一过程就可能花费很长时间,而等待的这段时间。我们无法做任何操作,比如点击退出登录按钮,直观感受就是点击没有任何反应,直到数据被响应回来。

二、常用 Effect (完善中)

Effect 指 Reduct Saga 中各种 Api(take、call、put、select、fork、takeEveny、takeLatest...)

1. take 与 takeEvery 的区别

take

用于暂停 Generator 直到匹配指定的 action 被发起。当匹配到对应的 action 时,Generator 将继续执行。

import { take, put } from 'redux-saga/effects';

function* mySaga() {
  yield take('SOME_ACTION');
  yield put({ type: 'OTHER_ACTION' });
}

上述代码中,take('SOME_ACTION') 表示 Generator 会在捕获到 'SOME_ACTION' 的 dispatch 时停止执行,然后继续执行后续的代码。

takeEvery

在每次匹配到指定的 action 时启动一个新的 saga。

import { takeEvery, put } from 'redux-saga/effects';

function* mySaga(action) {
    yield put({ type: 'OTHER_ACTION', payload: action.payload });
}

function* watchEvery() {
    yield takeEvery('SOME_ACTION', mySaga);
}

takeEvery 会监听每次 'SOME_ACTION' 被 dispatch,然后调用 mySaga,传递相应的 action。

总结:take 是等待单个 action 的 Effect,而 takeEvery 是在每次匹配到指定 action 时启动一个新的 saga。

2. takeEvery、takeLatest、takeLeading 的区别

takeLeading

在每次 Action 被触发时立即执行一次 Saga。如果在 Saga 期间再次触发相同的 Action,Saga 将被阻塞,直到当前的 Saga 完成才会再次执行。

takeEvery

  • 多次执行: takeEvery 会在每次匹配到指定 action 时都启动一个新的 saga,即使前一个执行还未完成。
  • 并发执行: 如果多个相同的 action 被 dispatch,多个对应的 saga 可能会同时执行。
  • 适用场景: 适用于需要处理每次匹配到的 action,不考虑执行时机的场景。
import { takeEvery, put } from 'redux-saga/effects';

function* mySaga(action) {
    yield put({ type: 'OTHER_ACTION', payload: action.payload });
}

function* watchEvery() {
    yield takeEvery('SOME_ACTION', mySaga);
}

takeLatest

  • 最新执行: takeLatest 在每次匹配到指定 action 时都会取消之前正在执行的 saga,只执行最新的。
  • 防抖效果: 适用于处理频繁发起的 action,确保只有最新的 action 会被处理,可以实现一种防抖效果。
  • 适用场景: 适用于需要关注最新 action 并避免并发执行的场景。
import { takeLatest, put } from 'redux-saga/effects';

function* mySaga(action) {
    yield put({ type: 'OTHER_ACTION', payload: action.payload });
}

function* watchLatest() {
    yield takeLatest('SOME_ACTION', mySaga);
}

总的来说,takeEvery 适用于并发执行的场景,而 takeLatest 适用于确保只处理最新 action 的场景,避免并发执行的问题。选择使用哪个取决于具体的业务需求。

fork是非阻塞的,将call替换为fork就可以解决这一问题,其效果更像是web worker。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

麦田里的POLO桔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值