Короткий ответ:
React гарантирует, что ссылки установлены раньше componentDidMountили componentDidUpdateхуки. Но только для детей, которые действительно были обработаны .
componentDidMount() {
}
componentDidUpdate() {
}
render() {
return <div ref={/* ... */} />;
}
Обратите внимание, это не означает, что «React всегда устанавливает все ссылки перед запуском этих хуков».
Давайте посмотрим на несколько примеров, когда ссылки не устанавливаются.
Ссылки не устанавливаются для элементов, которые не были отображены
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 нет возможности узнать, что при каком-то другом условии он возвращает что-то еще, к чему нужно прикрепить ссылку. Там нет также ничего установить реф на:<div> элемент не был создан , так как render()метод сказал , что это не должно быть оказано.
Итак, в этом примере componentDidMountбудет срабатывать только . Однако при this.state.loadingизменении наfalse вы this._setRefсначала увидите прикрепленное, а затем componentDidUpdateсработаете.
Остерегайтесь других компонентов
Обратите внимание: если вы передадите дочерние элементы с refs другим компонентам, есть вероятность, что они делают что-то, что предотвращает рендеринг (и вызывает проблему).
Например, это:
<MyPanel>
<div ref={this.setRef} />
</MyPanel>
не будет работать, если MyPanelне включить props.childrenв его вывод:
function MyPanel(props) {
return <h1>Oops, no refs for you today!</h1>;
}
Опять же, это не ошибка: React не на что будет устанавливать ссылку, потому что элемент DOM не был создан .
Ссылки не устанавливаются перед жизненными циклами, если они передаются вложенному ReactDOM.render()
Подобно предыдущему разделу, если вы передадите дочерний элемент со ссылкой другому компоненту, возможно, что этот компонент может сделать что-то, что не позволяет вовремя прикрепить ссылку.
Например, возможно, он не возвращает потомка 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для сохранения ref в обратном вызове ref, так как он асинхронный и до того, как он "завершится", componentDidMountбудет выполнен первым.
Все еще проблема?
Если ни один из приведенных выше советов не помог, сообщите о проблеме в React, и мы рассмотрим.
thisиз лексической области за пределами вашего класса. Попробуйте избавиться от синтаксиса стрелочной функции для методов вашего класса и посмотрите, поможет ли это.