React Hooks中如何在特定动作后创建RxJS订阅并取消订阅?
如何在特定动作触发RxJS订阅并正确取消
要实现表单提交成功后启动RxJS倒计时,同时妥善处理订阅取消(避免内存泄漏、重复订阅),可以按以下方式修改代码:
核心思路
- 用
useRef持久存储订阅实例,方便后续取消 - 组件卸载时自动清理订阅
- 再次触发提交时,先取消旧订阅再创建新的
- 使用函数式更新状态避免闭包问题
修改后的完整代码
import { interval, Subscription } from 'rxjs'; import { take } from 'rxjs/operators'; import { useState, useEffect, useRef } from 'react'; // 导入你的sendCode和message组件 const YourComponent = () => { const [timing, setTiming] = useState<number>(60); // 用useRef保存订阅实例,初始为null const countdownSubscription = useRef<Subscription | null>(null); // 组件卸载时强制取消订阅,防止内存泄漏 useEffect(() => { return () => { countdownSubscription.current?.unsubscribe(); }; }, []); const sendCodeOnceSubmit = async (phone: number) => { // 先取消之前的倒计时(如果存在),避免多个倒计时同时运行 countdownSubscription.current?.unsubscribe(); const res = await sendCode(phone); if (res.code !== 200) { message.error(`${res.message}`); // 请求失败时重置倒计时 setTiming(60); } else { // 重置倒计时初始值 setTiming(60); // 创建新的订阅并保存到useRef中 countdownSubscription.current = interval(1000) .pipe(take(60)) // 只取60次值,对应60秒倒计时 .subscribe({ next: () => { // 用函数式更新获取最新状态,避免闭包问题 setTiming(prevTiming => prevTiming - 1); }, complete: () => { // 倒计时结束后重置状态,并清空订阅引用 setTiming(60); countdownSubscription.current = null; } }); } }; // 组件其他逻辑... return ( // 你的表单JSX... ); }; export default YourComponent;
关键细节说明
- useRef存储订阅:
useRef的.current属性在组件生命周期内不会被重置,能稳定保存订阅实例,不会像普通变量那样每次渲染都重新初始化。 - 组件卸载清理:
useEffect的返回函数会在组件卸载时执行,这里调用unsubscribe()确保订阅被正确取消,防止内存泄漏。 - 重复提交处理:每次触发
sendCodeOnceSubmit时先取消旧订阅,避免同一时间存在多个倒计时流,导致状态混乱。 - 函数式更新状态:直接用
setTiming(prev => prev - 1),而不是依赖外部的timing变量,解决了闭包导致的状态更新滞后问题。
内容的提问来源于stack exchange,提问作者wu bevis
相关产品推荐
相关产品推荐

