user12113809
user12113809

Reputation:

Parent state change generates a child re-render when the child does not have that state as a prop [Virtual DOM]

I've been doing some tests about changing a parent state property affecting directly with a new child re-render.

  1. Using onMouseEnter/onMouseLeave methods of a parent to change "isMouseOver" state --> Affects to child.

  2. Using useHover hook implemented using parent ref and event listener --> Affects to child

  3. Using css Hover --> Doesn't affect to child

The problem is that parent "isMouseOver" state or "isHovered" shouldn't affect to child because it's not a child prop.

In child, I'm passing as a props "top" and "left", both setted in child styles, calculating a new top.

For each parent hover, child handleTop is triggered, giving me to undersantd that child is re-rendered for each state change of parent but in my head it has no logic because it should not re-render thanks to the virtual dom, so maybe is due the way of setting child style.

CODE:

PARENT

export default function App() {
  const [isMouseOver, setIsMouseOver] = useState<boolean>(false);
  const [elementRef, isHovered] = useHover();

  const handleMouseOver = (isMouseOver: boolean): void => {
    setIsMouseOver(isMouseOver);
  };

  return (
    <div className={styles.App}>
      <div
        className={
          isMouseOver
            ? `${styles.element} ${styles.element___isMouseOver}`
            : styles.element
        }
        onMouseEnter={() => handleMouseOver(true)}
        onMouseLeave={() => handleMouseOver(false)}
      >
        {/* <div ref={elementRef} className={styles.element}> */}
        {/* <div className={styles.element}> */}
        <PopOverComponent top={0} left={200} />
      </div>
    </div>
  );
}

CHILD:

interface PopOverComponentProps {
  top: number;
  left: number;
}

export default function PopOverComponent({ top, left }: PopOverComponentProps) {
  useEffect(() => {
    console.log("top changes", top);
  }, [top]);

  const handleTop = (): number => {
    const newTop = top + 25;
    console.log("handleTop is triggered", newTop);

    return newTop;
  };

  return (
    <div style={{ top: handleTop(), left: left }} className={styles.popOver} />
  );
}

Playground

Thank you very much in advance

Upvotes: 1

Views: 1262

Answers (1)

Asif vora
Asif vora

Reputation: 3347

You can use React.memo() for avoiding re-rendering stuff in react component. Avoiding React component re-renders with React.memo

Example :

interface PopOverComponentProps {
  top: number;
  left: number;
}

 const PopOverComponent = React.memo(({ top, left }: PopOverComponentProps) => {
  useEffect(() => {
    console.log("top changes", top);
  }, [top]);

  const handleTop = (): number => {
    const newTop = top + 25;
    console.log("handleTop is triggered", newTop);

    return newTop;
  };

  return (
    <div style={{ top: handleTop(), left: left }} className={styles.popOver} />
  );
})

export default PopOverComponent

Upvotes: 1

Related Questions