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).
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 });
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With