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?
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
).
CodePudding user response:
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 });
}
}
CodePudding user response:
keydown
event always fires first, so it's enough to check for it
useEffect(() => {
console.log("keyEvent:", state);
}, [state.keyEvent]);