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]);
