You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React-Redux+RxJS:SendSMSEffect中setTimeout无法触发setDisabled问题

Got it, let's break down why your setTimeout isn't firing the setDisabled(false) action, and fix it properly.

The Core Issue

Your line of(setTimeout(() => setDisabled(false), 5000)) is the problem. When you wrap setTimeout in of(), RxJS immediately emits the timer ID returned by setTimeout as an Observable value—it doesn't wait for the 5-second delay to run your setDisabled action. Native browser timers don't play nicely with RxJS streams directly; you need to use RxJS's built-in operators for timing logic.

Fix 1: Replace setTimeout with RxJS timer

Use timer(5000) instead—it creates an Observable that emits a single value after 5 seconds. Then map that value to your setDisabled(false) action to ensure it runs only after the delay.

Fix 2: Fix Stream Completeness

Right now, if payload.smsType !== smsType.Validation, your switchMap doesn't return any Observable. This breaks the entire stream, so the subsequent hideLoader and delay logic never runs. We'll add an EMPTY Observable to handle that case, and a catchError to ensure failures don't stop the rest of the flow.

Corrected Code

import { timer, EMPTY } from 'rxjs';
// ... 其他必要导入

export const sendSMSEffect: Epic<IFluxPayloadAction<any, string>, any, IAppState> = (action$, state$) => action$.pipe( 
  ofType(SMSActions.SendSMS),
  map((action) => action.payload),
  map((payload) => { 
    const mobile:string = getMobilePhoneFromPersonalDetails(state$.value);
    return {mobile, smsType:payload};
  }),
  switchMap((payload) => { 
    const smsSelector = getSmsDetailsSelector(state$.value);
    const smsContent = retrieveSms(payload.smsType, smsSelector);
    return concat(
      // 可选:如果触发发送时还没禁用按钮,在这里添加
      // of(setDisabled(true)),
      of(showLoader(sms.sendingSMS)),
      from(sendSMSService(smsContent, payload.mobile, true)).pipe(
        switchMap((response:ISMSResponse) => { 
          const wasSMSSent = response.Sent === 'Y' ? true : false;
          if (payload.smsType === smsType.Validation) {
            return concat(
              of(wasValidationSMSSent(wasSMSSent)),
              of(updateSMSValidationRetry())
            );
          }
          // 处理非Validation类型的短信,返回空流保持完整
          return EMPTY;
        }),
        // 捕获请求错误,避免流中断
        catchError(() => EMPTY)
      ),
      of(hideLoader(sms.sendingSMS)),
      // 5秒后发出启用按钮的动作
      timer(5000).pipe(map(() => setDisabled(false)))
    );
  })
);

Extra Notes

  • Make sure you import timer and EMPTY from rxjs—they're core operators for this logic.
  • If you haven't already, add of(setDisabled(true)) at the start of the concat to disable the button immediately when the send action triggers.
  • The catchError ensures that even if the SMS request fails, the loader hides and the button re-enables after 5 seconds.

内容的提问来源于stack exchange,提问作者Fraser Steel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 22:07:52