Коротка відповідь:
Гарантуйте 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
викличеться.
Слідкуйте за іншими компонентами
Зверніть увагу, що якщо ви передаєте дочірні елементи з реф. До інших компонентів, існує ймовірність, що вони роблять щось, що заважає візуалізації (і спричиняє проблему).
Наприклад, це:
<MyPanel>
<div ref={this.setRef} />
</MyPanel>
не працювало б, якщо MyPanel
б не включило props.children
у свій результат:
function MyPanel(props) {
return <h1>Oops, no refs for you today!</h1>;
}
Знову ж, це не помилка: для React не було б чого встановити ref, оскільки елемент 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>
з посиланням фактично включається у вивід рендеринга.
Отже, якщо ви зіткнулися з цією проблемою, вам потрібно переконатися, що між вашим компонентом та посиланням немає нічого, що може затримати візуалізацію дочірніх елементів.
Не використовуйте setState
для зберігання посилань
Переконайтесь, що ви не використовуєте setState
для зберігання посилання у зворотному виклику ref, оскільки він є асинхронним, і до того, як він буде «закінчений», componentDidMount
буде виконаний першим.
Все ще є проблемою?
Якщо жодна з наведених вище порад не допоможе, подайте проблему в React, і ми подивимось.
this
з лексичної області за межами вашого класу. Спробуйте позбутися синтаксису функції стрілки для методів вашого класу і перевірте, чи це допомагає.