As Steffen Frank correctly pointed out, using DOM events is often risky...
I used another strategy using the React onMouseMove on the main container, in index.js
const [hoveredClasses, setHoveredClasses] = useState([]);
const handleMouseMove = (e) => {
var x = e.clientX;
var y = e.clientY;
let elementMouseIsOver =
document.elementFromPoint(x, y)?.closest(".c-justified-box") ||
document.elementFromPoint(x, y)?.closest(".tooltip");
let classes = elementMouseIsOver?.classList
? Array.from(elementMouseIsOver?.classList)
: [];
console.log(
"[ c-justified-box ] > Mouse Move ----------------------------->",
x,
y,
elementMouseIsOver,
classes
);
// check if we need to cal setState
if (
(classes.length === 0 && hoveredClasses.length !== 0) ||
!classes.every((classItem) => hoveredClasses.includes(classItem))
) {
setHoveredClasses(classes);
}
};
And
<main className={styles.main} onMouseMove={handleMouseMove}>
So on mouse move:
- we get the element under the mouse
- we check if that element classes are exactly the same as the classes we previously stored in state.
If no, we set the state with those classes. That triggers a redraw for the whole main component, so each of the JustifiedBox are also redrawn.
We are passing them the hoveredClasses array... so within each of them, a boolean is set to decide to show the modal or not.
const showFloatingDetailPanel = props.hoveredClasses.includes(
props.new_Album.slug
);
Has you can see... We use the props.new_Album.slug which was already used as a div class for c-justified-box
className={"c-justified-box" + " " + props.new_Album.slug}
We just need to also pass it to the AlbumDetailsPanel as a props, to do the same with the modal main div.
JustifiedBox.js:
<AlbumDetailsPanel
slug={props.new_Album.slug}
t_ref={floating}
//...
AlbumDetailPanel.js:
<div
className={"tooltip" + " " + props.slug}
So every times the hoveredClasses do not match, we setState... And that state will be used by each component to decide to show their associated AlbumDetailsPanel. It is now impossible to have more than on shown.
And this simplifies everything in JustifiedBox.js, since we got rid of:
handleAlbumMouseEnter
handleAlbumMouseOut
handleTooltipMouseEnter
handleTooltipMouseOut
- the 3
useEffect
The updated CodeSandbox