Last active
September 26, 2024 23:16
-
-
Save VanTanev/bed8bdea27924ae3d2f1710b296067b6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// optimized, only this component rerenders on mouse enter/leave and on ctrl press, | |
// its children do not | |
export function ReactToHoverWithCtrl() { | |
const isCtrlPressed = useIsCtrlPressed() // assume this uses sync external store | |
const [isHovered, setIsHovered] = useState(false) | |
// this must be computed here, and eg inside useMemo() | |
const isCtrlAndHover = isCtrlPressed && isHovered; | |
// Can alternatively be done with React.memo on the expensive components, | |
// but I kind of prefer this approach, we now keep all the complexity in one component | |
// without needing to spread React.memo throughout the codebase. | |
// | |
// In any case, the whole tree below this component is now rerendered only | |
// if the (isCtrlPressed && isHovered) condition changes, and the rerender of this | |
// component itself should be basically free | |
const children = useMemo(() => { | |
if (isCtrlAndHover) { | |
return <SomeExpensiveComponent /> | |
} else { | |
return <OtherExpensiveComponent /> | |
} | |
}, [isCtrlAndHover]) | |
return <div | |
onMouseEnter={() => setIsHovered(true)} | |
onMouseLeave={() => setIsHovered(false)} | |
{/* below can be omitted if we're not doing SSR */} | |
ref={useCallback((node) => { | |
if (!node) return | |
let current = document.querySelector(":hover") | |
if (!current) return | |
do { | |
if (node === current) { | |
setIsHovered(true) | |
return | |
} | |
} while ((current = current.parentNode)) | |
}, [])}>{children}</div> | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment