Реакция: как дождаться, пока ссылка не станет доступной при вставке (рендеринге) во втором контейнере
РЕДАКТИРОВАТЬ: лучшее объяснение
Контекст:
Я получаю простой 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
*/