Проблема
Я устанавливаю реакцию, ref
используя определение встроенной функции
render = () => {
return (
<div className="drawer" ref={drawer => this.drawerRef = drawer}>
то в componentDidMount
DOM ссылка не установлена
componentDidMount = () => {
// this.drawerRef is not defined
Насколько я понимаю, ref
обратный вызов должен выполняться во время монтирования, однако добавление console.log
выражений componentDidMount
вызывается перед функцией обратного вызова ref.
Другие образцы кода, на которые я смотрел, например, это обсуждение на github указывает на то же предположение, его componentDidMount
следует вызывать после любых ref
обратных вызовов, определенных в render
, это даже указано в разговоре
Итак, componentDidMount запускается после выполнения всех обратных вызовов ref?
Да.
Я использую реакцию 15.4.1
Что-то еще я пробовал
Чтобы убедиться, что ref
функция вызывается, я попытался определить ее в классе как таковой
setDrawerRef = (drawer) => {
this.drawerRef = drawer;
}
затем в render
<div className="drawer" ref={this.setDrawerRef}>
Ведение журнала консоли в этом случае показывает, что обратный вызов действительно вызывается после componentDidMount
источник
this
из лексической области за пределами вашего класса. Попробуйте избавиться от синтаксиса стрелочной функции для методов вашего класса и посмотрите, поможет ли это.render
и, следовательно, должны были использовать ееcomponentDidUpdate
, посколькуcomponentDidMount
не является частью жизненного цикла обновления . Вероятно, это не ваша проблема, но подумал, что ее стоит поднять как потенциальное решение.ref callbacks are invoked before componentDidMount or componentDidUpdate lifecycle hooks.
но это не похоже на правду :(ref = {ref => { this.drawerRef = ref }}
2. даже ссылки вызываются перед componentDidMount; ref можно получить только после первоначального рендеринга при рендеринге div в вашем случае. Таким образом, вы должны иметь возможность получить доступ к ссылке на следующем уровне, т.е. в componentWillReceiveProps, используяthis.drawerRef
3. Если вы попытаетесь получить доступ до первоначального монтирования, вы получите только неопределенные значения ref.Ответы:
Короткий ответ:
React гарантирует, что ссылки установлены раньше
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 всегда устанавливает все ссылки перед запуском этих хуков».
Давайте посмотрим на несколько примеров, когда ссылки не устанавливаются.
Ссылки не устанавливаются для элементов, которые не были отображены
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) { // ignore props.children 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, и мы рассмотрим.
источник
Другое наблюдение за проблемой.
Я понял, что проблема возникла только в режиме разработки. После дополнительного исследования я обнаружил, что отключение
react-hot-loader
в моей конфигурации Webpack предотвращает эту проблему.Я использую
И это электронное приложение.
Моя частичная конфигурация разработки Webpack
const webpack = require('webpack') const merge = require('webpack-merge') const baseConfig = require('./webpack.config.base') module.exports = merge(baseConfig, { entry: [ // REMOVED THIS -> 'react-hot-loader/patch', `webpack-hot-middleware/client?path=http://localhost:${port}/__webpack_hmr`, '@babel/polyfill', './app/index' ], ... })
Это стало подозрительным, когда я увидел, что использование встроенной функции в render () работает, но использование связанного метода дает сбой.
Работает в любом случае
class MyComponent { render () { return ( <input ref={(el) => {this.inputField = el}}/> ) } }
Сбой при использовании response-hot-loader (ссылка не определена в componentDidMount)
class MyComponent { constructor (props) { super(props) this.inputRef = this.inputRef.bind(this) } inputRef (input) { this.inputField = input } render () { return ( <input ref={this.inputRef}/> ) } }
Честно говоря, горячую перезагрузку часто бывает проблематично получить "правильно". Благодаря быстрому обновлению инструментов разработки каждый проект имеет свою конфигурацию. Возможно, мою конкретную конфигурацию можно исправить. Я дам вам знать здесь, если это так.
источник
Проблема также может возникнуть, когда вы пытаетесь использовать ссылку на отключенный компонент, например, используя ссылку в setinterval, и не очищаете установленный интервал во время размонтирования компонента.
componentDidMount(){ interval_holder = setInterval(() => { this.myref = "something";//accessing ref of a component }, 2000); }
всегда очищать интервал, например,
componentWillUnmount(){ clearInterval(interval_holder) }
источник