Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent useEffect hook with two dependencies from running twice

I want to use two properties of a state in the same useEffect hook:

state.keyEvent: keydown from document (to listen to commands like Ctrl + R).

state.value: value of input (to perform a text search).

import { useEffect, useReducer } 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 });
  }

  function handleDocumentKeyDown(event: any) {
    updateState({ keyEvent: event });
  }

  useEffect(() => {
    document.addEventListener("keydown", handleDocumentKeyDown);

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

  useEffect(() => {
    console.log("keyEvent:", state);
  }, [state]);

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

export default App;

This works—except the useEffect hook runs twice when I type in the input element.

I think I have to tell the code: If state.value updates, ignore state.keyEvent and don't run the hook again.

Is that correct? If so, how to accomplish this?

Edit useState same value rerender (forked)

Note: if I put state.keyEvent and state.useEffect in different useEffect hooks, I won't be able to have the latest value of state.value in the hook containing state.keyEvent (because the hook containing state.keyEvent will run first than the hook containing state.value).

like image 599
alexchenco Avatar asked Nov 02 '25 06:11

alexchenco


1 Answers

If you want to just listen for commands like ctrl+r, you can pre-filter them in your keyboard handler and exclude from being pushed to the input:

  ....

  function isCommand(event: KeyboardEvent) {
    return event.ctrlKey && event.key === 'r';
  }

  function handleDocumentKeyDown(event: KeyboardEvent) {
    if (isCommand(event)) {
      event.preventDefault();
      updateState({ keyEvent: event });
    }
  }
like image 141
Nikolay Avatar answered Nov 04 '25 18:11

Nikolay



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!