Ron
Ron

Reputation: 6755

React useRef changes didn't cause the component rerender which depends on ref.current

The intention of this Upsell component is to render something in certain area (portal). So it will accept a DOM element, and createPortal on that. The problem is the widgetDom is always null and cause the createPortal didn't get chance to render.

In consumer I use useRef and pass the widgetRef.current to the Upsell component. I am expecting when the widgetRef attached the real element, widgetRef.current changes will cause the Upsell component rerender, but it is not happening.

Can you have a look the following code or the sandbox? Thank you!

const Upsell: React.FC<{ widgetDom: HTMLElement | null }> = ({ widgetDom }) => {
  return (
    <div className="box">
      <h1>Order Summary</h1>
      {widgetDom && createPortal(<h2>Order Summary Widget</h2>, widgetDom)}
    </div>
  );
};

export default function App() {
  const widgetDom = React.useRef<HTMLElement | null>(null);
  return (
    <div className="App">
      <div className="box" style={{ width: "60%" }}>
        <div className="box">
          <h1>Ad Selection</h1>
        </div>
        <Upsell widgetDom={widgetDom.current} />
      </div>
      <div className="box" style={{ width: "40%" }}>
        <div id="orderSummaryWidget" ref={widgetDom} className="box"></div>
      </div>
    </div>
  );
}

Upvotes: 2

Views: 3390

Answers (1)

gerrod
gerrod

Reputation: 6637

Refs by design don't cause a re-render when they change:

Keep in mind that useRef doesn’t notify you when its content changes. Mutating the .current property doesn’t cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a callback ref instead.

The easiest fix is to use state instead of a ref:

export default function App() {
  const [widgetDom, setWidgetDom] = useState<HTMLElement | null>(null);

  return (
    <div className="App">
      <div className="box" style={{ width: "60%" }}>
        <div className="box">
          <h1>Ad Selection</h1>
        </div>
        <Upsell widgetDom={widgetDom} />
      </div>
      <div className="box" style={{ width: "40%" }}>
        <div id="orderSummaryWidget" ref={setWidgetDom} className="box"></div>
      </div>
    </div>
  );
}

Upvotes: 3

Related Questions