Ответ 1
Короткий ответ:
React гарантирует, что refs устанавливаются перед hook- componentDidUpdate
componentDidMount
или componentDidUpdate
. Но только для детей, которые действительно получили.
componentDidMount() {
// can use any refs here
}
componentDidUpdate() {
// can use any refs here
}
render() {
// as long as those refs were rendered!
return <div ref={/* ... */} />;
}
Обратите внимание, что это не означает, что "React всегда устанавливает все значения refs перед запуском этих крючков".
Давайте посмотрим на некоторые примеры, когда refs не устанавливаются.
Refs dont get set для элементов, которые werent rendered
React будет вызывать только обратные вызовы ref для элементов, которые вы фактически вернули из рендеринга.
Это означает, что если ваш код выглядит
render() {
if (this.state.isLoading) {
return <h1>Loading</h1>;
}
return <div ref={this._setRef} />;
}
и первоначально this.state.isLoading
true
, вы не должны ожидать, что this._setRef
будет вызван перед componentDidMount
.
Это должно иметь смысл: если ваш первый рендер возвратил <h1>Loading</h1>
, для React не существует возможного способа узнать, что при каком-либо другом условии он возвращает что-то еще, для которого требуется привязка ref. Также нет ничего, чтобы установить ref: элемент <div>
не был создан, потому что метод render()
сказал, что он не должен отображаться.
Таким образом, в этом примере будет запущен только componentDidMount
. Однако, когда this.state.loading
изменится на false
, вы увидите, что this._setRef
прикреплен первым, а затем будет this._setRef
componentDidUpdate
.
Следите за другими компонентами
Обратите внимание: если вы передаете детям с refs до других компонентов, есть шанс, что они делают что-то, что предотвращает рендеринг (и вызывает проблему).
Например, это:
<MyPanel>
<div ref={this.setRef} />
</MyPanel>
не будет работать, если MyPanel
не включил props.children
в свой вывод:
function MyPanel(props) {
// ignore props.children
return <h1>Oops, no refs for you today!</h1>;
}
Опять же, это не ошибка: для React не было бы никакого задания ref, потому что элемент DOM не был создан.
Refs dont get set before lifecycles, если theyre передано вложенное ReactDOM.render()
Как и в предыдущем разделе, если вы передаете дочерний элемент с ссылкой на другой компонент, возможно, что этот компонент может сделать что-то, что предотвращает своевременное добавление ссылки.
Например, возможно, что он не возвращает ребенка из render()
, а вместо этого вызывает ReactDOM.render()
в ReactDOM.render()
жизненного цикла. Вы можете найти пример этого здесь. В этом примере мы делаем:
<MyModal>
<div ref={this.setRef} />
</MyModal>
Но MyModal
выполняет ReactDOM.render()
в своем жизненном цикле componentDidUpdate
:
componentDidUpdate() {
ReactDOM.render(this.props.children, this.targetEl);
}
render() {
return null;
}
Начиная с React 16, такие вызовы визуализации верхнего уровня в течение жизненного цикла будут отложены до тех пор, пока не будут выполняться жизненные циклы для всего дерева. Это объясняет, почему вы не видите ссылки, связанные со временем.
Решение этой проблемы заключается в использовании порталов вместо вложенных вызовов ReactDOM.render
:
render() {
return ReactDOM.createPortal(this.props.children, this.targetEl);
}
Таким образом, наш <div>
с ref фактически включен в вывод рендеринга.
Поэтому, если вы столкнулись с этой проблемой, вам нужно проверить, нет ли между вашим компонентом и ссылкой, что может задержать рендеринг детей.
Не используйте setState
для хранения setState
Убедитесь, что вы не используете setState
для хранения ref в setState
ref, так как он асинхронен и до его завершения " componentDidMount
будет выполнен первым.
Еще проблема?
Если ни один из приведенных выше советов не поможет, напишите о проблеме в React, и мы посмотрим.