Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React: How to wait until ref is available when inserted (rendered) within a second container

EDIT: better explanation

The context:

I receive some plain HTML code from a 3rd server, which I want to

  • insert in my React app
  • modify it

The vanilla JS approach

  • I can modify the string with regex and add any HTML tag with an id
  • Then I can modify these elements through getElementById, as usual

The React approach

  • I shouldn't use the DOM
  • Then I should insert within the string some components that have a React ref inside
  • The opposite (to insert some React components as plain HTML) would be through ReactDOMServer.renderToString
  • So, when I inject the components with ReactDOM.render(), the problem is that the render method takes its time, so that if in the next line I try to use the ref that exists in the inserted component, is not yet there

The question

  • How to do it? Usually I would put the code within a useEffect with a [] dependencies, but here I am rendering the component when the app is already mounted
  • A quick workaround is to just do an async wait of 500 ms, and then I can access the ref, but for sure there has to be something better

This code fails, because when the ref is rendered it is still not available, so ref.current is undefined

How can I wait for it?

codesandbox

EDIT: I provide the code that works but through direct DOM, which I assume should be avoided

import React, { useRef, useEffect } from "react";
import ReactDOM from "react-dom";

export default function App() {
  const myref = useRef();

  useEffect(() => {
    const Com = () => <div ref={myref}>hello</div>;
    ReactDOM.render(<Com />, document.getElementById("container"));
    console.log(myref.current); // undefined
    document.getElementById('container').textContent = "direct DOM works"

   // the next line fails since the ref is not yet available
   // myref.current.textContent = "but this REF is not available"; // fails
  }, []);

  const plainhtml = '<div><div id="container"></div><div>some more content</div><div id="another">even more content</div></div>'; // this is some large HTML fetched from an external server

  return (
    <div>
      <h1>Hello CodeSandbox</h1>
      <div dangerouslySetInnerHTML={{ __html: plainhtml }} />
    </div>
  );
}
like image 336
GWorking Avatar asked Jan 28 '26 21:01

GWorking


2 Answers

useEffect with empty dependency array executes after the first render, therefore you will get the DOM ref in the callback:

const htmlString = '<div id="container">Hello</div>';

export default function App() {
  const myRef = useRef();

  useEffect(() => {
    if (myRef.current) {
      myRef.current.textContent = 'whats up';
    }
    console.log(myRef.current);
  }, []);

  return (
    <div>
      <div ref={myRef} dangerouslySetInnerHTML={{ __html: htmlString }} />
      <div dangerouslySetInnerHTML={{ __html: htmlString }} />
    </div>
  );
}

/* App renders:
whats up
Hello
*/

Edit nervous-glade-8rtxb

like image 61
Dennis Vash Avatar answered Jan 30 '26 11:01

Dennis Vash


I need to use a callback ref but encapsulating it within useCallback to make sure it only rerenders with the dependencies indicated (i.e. none []), so that it is only executed when the component changes (as explained here)

codesandbox

import React, { useEffect, useCallback } from "react";
import ReactDOM from "react-dom";

export default function App() {
  const measuredRef = useCallback(node => {
    if (node !== null) {
      node.textContent = "useCallback DOM also works";
    }
  }, []);

  useEffect(() => {
    const Com = () => <div ref={measuredRef}>hello</div>;
    ReactDOM.render(<Com />, document.getElementById("container"));
    document.getElementById("container").textContent = "direct DOM works";
  }, []);

  const plainhtml = '<div id="container"></div>';

  return (
    <div>
      <h1>Hello CodeSandbox</h1>
      <div dangerouslySetInnerHTML={{ __html: plainhtml }} />
    </div>
  );
}
like image 34
GWorking Avatar answered Jan 30 '26 10:01

GWorking



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!