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

解决含两个依赖项的useEffect钩子重复执行两次的问题

解决方案

你的思路是正确的。当前代码中第二个useEffect依赖整个state,所以不管是输入框value变化还是keyEvent按键变化,都会触发该钩子,导致输入时出现不必要的重复执行。我们需要让这个钩子仅在keyEvent变化时触发,同时确保能获取到最新的value值,以下是两种可行的实现方案:

方案一:基于state.keyEvent的依赖触发

这种方案通过调整useEffect的依赖项,仅监听state.keyEvent的变化,同时利用React状态更新的特性,确保触发时能拿到最新的value值:

import { useEffect, useReducer, useCallback } from "react";

const App = () => {
  const initialState = { keyEvent: {}, value: "builder" };
  const [state, updateState] = useReducer(
    (state: any, updates: any) => ({ ...state, ...updates }),
    initialState
  );

  function handleInputChange(event: any) {
    updateState({ value: event.target.value });
  }

  // 用useCallback包裹事件处理函数,确保引用稳定,避免重复绑定/移除事件
  const handleDocumentKeyDown = useCallback((event: any) => {
    updateState({ keyEvent: event });
  }, [updateState]);

  useEffect(() => {
    document.addEventListener("keydown", handleDocumentKeyDown);
    return () => {
      document.removeEventListener("keydown", handleDocumentKeyDown);
    };
  }, [handleDocumentKeyDown]);

  // 仅在state.keyEvent变化时触发effect,此时state.value为最新值
  useEffect(() => {
    // 过滤出需要监听的快捷键,比如Ctrl+R
    if (state.keyEvent.ctrlKey && state.keyEvent.key === 'r') {
      console.log('执行搜索,搜索内容:', state.value);
      // 这里添加你的搜索逻辑
      state.keyEvent.preventDefault(); // 阻止浏览器默认刷新行为
    }
  }, [state.keyEvent]);

  return (
    <div>
      <input
        id="input"
        type="text"
        onChange={handleInputChange}
        value={state.value}
      />
    </div>
  );
};

export default App;

说明:

  • 用useCallback包裹handleDocumentKeyDown,保证函数引用稳定,避免组件每次渲染都重新绑定/移除事件,解决闭包和内存泄漏问题。
  • 第二个useEffect的依赖改为[state.keyEvent],只有当按键事件触发导致keyEvent更新时才执行,此时state.value已经是最新的输入内容,满足快捷键触发时使用最新搜索值的需求。
  • 添加快捷键判断逻辑,避免任意按键都触发执行,符合你监听特定命令的场景。

方案二:使用useRef实时保存最新value

这种方案直接在按键事件处理函数中完成逻辑,通过useRef实时保存最新的输入值,避免不必要的state更新和组件渲染:

import { useEffect, useReducer, useCallback, useRef } from "react";

const App = () => {
  const initialState = { value: "builder" };
  const [state, updateState] = useReducer(
    (state: any, updates: any) => ({ ...state, ...updates }),
    initialState
  );
  // 用ref实时保存最新的输入值
  const valueRef = useRef(state.value);

  function handleInputChange(event: any) {
    const newValue = event.target.value;
    updateState({ value: newValue });
    valueRef.current = newValue;
  }

  // 直接在事件处理函数中判断快捷键并执行逻辑
  const handleDocumentKeyDown = useCallback((event: any) => {
    if (event.ctrlKey && event.key === 'r') {
      console.log('执行搜索,搜索内容:', valueRef.current);
      // 这里添加你的搜索逻辑
      event.preventDefault(); // 阻止浏览器默认刷新行为
    }
  }, []);

  useEffect(() => {
    document.addEventListener("keydown", handleDocumentKeyDown);
    return () => {
      document.removeEventListener("keydown", handleDocumentKeyDown);
    };
  }, [handleDocumentKeyDown]);

  return (
    <div>
      <input
        id="input"
        type="text"
        onChange={handleInputChange}
        value={state.value}
      />
    </div>
  );
};

export default App;

说明:

  • 用useRef的current属性实时保存最新的输入值,不需要依赖state变化就能获取到最新内容。
  • 按键事件处理函数直接在触发时判断快捷键并执行逻辑,不需要更新keyEvent状态,减少了不必要的组件渲染,性能更优。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 13:10:25