This is an onscreen keyboard component.
I don’t feel like rewriting all the properties for a single key button every time because that would make the code look ugly, so I opted for an extra component that I treat as a "key button shorthand", problem is every time I press a key I notice the rest of the keys also rerender because of the state change, what’s an alternative solution to prevent this?
function Keyboard({ setState }: { setState: React.Dispatch<React.SetStateAction<string>> }) {
let keyInterval = useRef<NodeJS.Timeout>()
let keyTimeout = useRef<NodeJS.Timeout>()
function keyDownBehaviour(action: () => void) {
action()
keyTimeout.current = setTimeout(() => {
keyInterval.current = setInterval(() => {
action()
}, 50)
}, 500)
}
function keyUpBehaviour() {
clearInterval(keyInterval.current)
clearTimeout(keyTimeout.current)
}
function Key({ text }: { text: string }) {
return (
<Button
className="text-lg p-0 w-12"
variant="outline"
onMouseDown={() => keyDownBehaviour(() => setState(prevAnswer => prevAnswer + text))}
onMouseUp={keyUpBehaviour}
onMouseLeave={keyUpBehaviour}
>
{text}
</Button>
)
}
return (
<div className="flex flex-col mt-2 gap-2 w-auto">
<div className="flex gap-[0.2rem] sm:gap-2 w-auto">
<Key text="b" />
<Key text="a" />
</div>
</div>
)
}
3
Answers
You should not define components inside of your component but explicitly make it its own component. This way you can memoize the component.
From the react documentation: "
memo
lets you skip re-rendering a component when its props are unchanged."In your case you can use it like:
You can then just use it like
If the other keys are still rendering you might need to add a custom comparison function
You can prevent unnecessary rerenders in react using multiple hooks such as useCallback, useMemo, useLayOutEffect, useEffect but the most suitable method for the example you privided is
React.memo
which memoizes the rendered output of the component based on it’s props preventing unnecessary renders when props remains same.React is a super simple construct. Your App is a "tree of components" where the top component and is the only component without a parent.
Components (which are simply JS functions) will Render (i.e. the function will be executed) under only 2 cases:
Since your Keyboard component is being passed down a "set state function" of its parent (
setState
), and Keyboard calls that function, then the Keyboard is changing a state value in its parent…….which will cause the parent to re-render…
….which will cause ALL of the parent’s children to re-render…
…which will cause all of Keyboard’s children to re-render…
The question is: do you actually want (or need) to have Keyboard updating the state of Keyboard’s parent? Or is there a cleaner/better way to communicate the event press without updating state in the parent and causing a cascading of renders?