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

React.js+Socket.io实现用户正在输入功能的定时器异常问题

问题根因

你当前的代码存在两个核心问题,才会导致定时器不生效、状态闪烁:

  • timer变量没有做跨渲染持久化:如果是在React函数组件中直接用普通变量存储定时器ID,每次组件重渲染时该变量都会被重置,clearTimeout(timer)根本无法清除上一次按键触发的定时器,最终就会出现输入N个字符就打印N次test的情况。
  • 存在闭包状态陷阱:isTyping是组件响应式状态,每次渲染都会捕获当前渲染周期的状态值,高频输入时判断逻辑容易拿到过期的状态值,导致重复触发typing事件、状态闪烁。
修复实现

核心思路是用跨渲染不丢失的引用存储定时器ID,保证每次按键都能准确清除上一次的定时器,实现防抖效果,同时对齐状态更新和socket事件的触发时机:

import { useState, useRef, useEffect } from 'react';

// 组件内部逻辑
const [currentMessage, setCurrentMessage] = useState('');
const [isTyping, setIsTyping] = useState(false);
// 用ref存储定时器ID,跨渲染周期持久化保存,不会随重渲染重置
const typingTimerRef = useRef(null);

// 组件卸载时清除残留定时器,避免内存泄漏
useEffect(() => {
  return () => {
    if (typingTimerRef.current) {
      clearTimeout(typingTimerRef.current);
    }
  };
}, []);

const handleTyping = (e) => {
  // 每次输入先清除上一轮的定时器
  if (typingTimerRef.current) {
    clearTimeout(typingTimerRef.current);
  }

  setCurrentMessage(e.target.value);

  // 仅当当前不在输入状态时,才触发typing事件、更新状态
  if (!isTyping) {
    setIsTyping(true);
    socket.current.emit("typing", room);
  }

  // 写入新的定时器ID到ref中,3秒无操作才触发停止输入的逻辑
  typingTimerRef.current = setTimeout(() => {
    console.log("test");
    setIsTyping(false);
    socket.current.emit("stopTyping", room);
  }, 3000);
};

注意:如果room、socket实例存在动态变化的情况,需要根据实际场景调整逻辑依赖,避免拿到过期的实例值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 20:57:15