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

React中如何通过按钮和自定义事件(如WebSocket)更新变量?

解决方案:避免React中重复添加/移除事件监听更新状态

问题背景

需要在React组件中通过按钮点击和自定义事件(模拟WebSocket消息)更新计数器变量,但当前实现每次count变化都会重复执行事件监听的添加与移除,希望优化这一行为。

核心问题分析

原代码中useEffect的依赖数组包含count,导致每次count更新时都会重新创建事件处理函数并重新绑定监听,同时移除旧的监听。这不仅没必要,还可能带来性能损耗。

优化方案

方案1:使用useState的函数式更新(推荐)

利用setState的函数式更新特性,直接获取最新的状态值,无需在useEffect依赖中传入count,这样监听只需挂载一次。

修改后的完整代码:

import { useEffect, useState } from "react";

function App() {
  const [count, setCount] = useState(0);
  const [log, setLog] = useState([]);

  useEffect(() => {
    const onCustomEvent = () => {
      // 使用函数式更新,获取最新的prevCount
      setCount(prevCount => prevCount + 1);
    };
    logEvent('addEventListener');
    document.addEventListener('custom-event', onCustomEvent);

    return () => {
      logEvent('removeEventListener');
      document.removeEventListener('custom-event', onCustomEvent);
    };
  }, []); // 依赖数组为空,仅在组件挂载/卸载时执行

  function dispatchEvent() {
    document.dispatchEvent(new CustomEvent('custom-event'));
  }

  function logEvent(msg) {
    console.log(msg);
    // log也使用函数式更新,避免依赖log
    setLog(prevLog => [...prevLog, msg]);
  }

  return (
    <>
      <button onClick={() => setCount(prev => prev + 1)}>
        Increase counter
      </button>
      <button onClick={() => dispatchEvent()}>
        Dispatch a Custom Event, for example from a WebSockets
      </button>
      <div>
        I need to update a variable, for example counter, from buttons and from WebSockets.
        How to do it in react? Currently I use "useEffect" hook, but I don't think it's a correct way, 
        because I'd like to avoid addEventListener and removeEventListener on every counter change.
        Does anyone know a solution?
      </div>
      <div>count value is {count}</div>
      <div>Log (see console also):</div>
      {log.map((msg, i) => <div key={i}>{msg}</div>)}
    </>
  );
}

export default App;

方案2:使用useRef保存事件回调(适合复杂逻辑场景)

如果事件处理函数需要依赖其他状态,可通过useRef保存最新的回调函数,确保监听始终调用最新版本的逻辑,同时保持监听只挂载一次。

关键代码片段:

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

function App() {
  const [count, setCount] = useState(0);
  const [log, setLog] = useState([]);
  const eventCallbackRef = useRef();

  // 保存最新的回调到ref
  eventCallbackRef.current = () => {
    setCount(prev => prev + 1);
  };

  useEffect(() => {
    const onCustomEvent = () => {
      // 调用ref中保存的最新回调
      eventCallbackRef.current();
    };
    logEvent('addEventListener');
    document.addEventListener('custom-event', onCustomEvent);

    return () => {
      logEvent('removeEventListener');
      document.removeEventListener('custom-event', onCustomEvent);
    };
  }, []); // 依赖为空,仅挂载一次

  // 其余代码同方案1...
}

方案说明

  • 方案1是最简洁的解决方式,利用React内置的状态更新机制,直接避免不必要的依赖。
  • 方案2适用于事件处理逻辑需要依赖多个状态的场景,通过useRef确保回调始终是最新的,同时保持监听的稳定性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 09:07:22