解决含两个依赖项的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
相关产品推荐
相关产品推荐

