javascript - Förhindra att useEffect-hooken med två beroenden körs två gånger


Jag vill använda två egenskaper av ett tillstånd i samma useEffect-hook:

state.keyEvent: keydown från document (för att lyssna på kommandon som Ctrl + R).

state.value: värdet av input (för att utföra en textsökning).

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;

Detta fungerar—utom att useEffect-hooken körs två gånger när jag skriver i input-elementet.

Jag tror att jag måste säga till koden: Om state.value uppdateras, ignorera state.keyEvent och kör inte hooken igen.

Är det korrekt? Om så är fallet, hur uppnår man detta?

Obs: om jag placerar state.keyEvent och state.useEffect i olika useEffect-hooks, kommer jag inte att kunna ha det senaste värdet av state.value i hooken som innehåller state.keyEvent (eftersom hooken som innehåller state.keyEvent kommer att köras före hooken som innehåller state.value).

Svar #1

Om du bara vill lyssna efter kommandon som ctrl+r kan du förfiltrera dem i din tangentbordshanterare och utesluta dem från att skickas till inmatningen:

  ....

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

  function handleDocumentKeyDown(event: KeyboardEvent) {
    if (isCommand(event)) {
      event.preventDefault();
      updateState({ keyEvent: event });
    }
  }

Svar #2

keydown-händelsen utlöses alltid först, så det räcker att kolla efter den

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

Relaterade artiklar