Реакция: как дождаться, пока ссылка не станет доступной при вставке (рендеринге) во втором контейнере

РЕДАКТИРОВАТЬ: лучшее объяснение

Контекст:

Я получаю простой HTML-код с третьего сервера, который хочу

  • вставить в мое приложение React
  • изменить это

Подход ванильного JS

  • Я могу изменить строку с помощью регулярного выражения и добавить любой тег HTML с id
  • Затем я могу изменить эти элементы с помощью getElementById, по-прежнему

Подход React

  • Я не должен использовать DOM
  • Затем я должен вставить в строку некоторые компоненты, внутри которых есть ссылка на React.
  • Противоположное (вставить некоторые компоненты React как обычный HTML) будет через ReactDOMServer.renderToString
  • Итак, когда я ввожу компоненты с ReactDOM.render(), проблема в том, что render метод требует своего времени, поэтому, если в следующей строке я попытаюсь использовать ссылку, которая существует во вставленном компоненте, ее еще нет

Вопрос

  • Как это сделать? Обычно я помещал код вuseEffect с [] зависимости, но вот я rendering компонент, когда приложение уже смонтировано
  • Быстрый обходной путь - просто выполнить асинхронное ожидание в 500 мс, а затем я могу получить доступ к ref, но наверняка должно быть что-то лучше

Этот код не работает, потому что когда ref отображается, он все еще недоступен, поэтому ref.current не определено

Как мне этого дождаться?

коды

РЕДАКТИРОВАТЬ: я предоставляю код, который работает, но через прямую DOM, чего, как я полагаю, следует избегать

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>
  );
}

2 ответа

Решение

Мне нужно использовать обратный вызов ref, но инкапсулировать его вuseCallback чтобы убедиться, что он выполняет повторную визуализацию только с указанными зависимостями (т.е. []), так что он выполняется только при изменении компонента (как описано здесь)

коды

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>
  );
}

useEffectс пустым массивом зависимостей выполняется после первого рендеринга, поэтому вы получите ссылку на DOM в обратном вызове:

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
*/

Редактировать нервную поляну-8rtxb

Другие вопросы по тегам