Problema
Estoy configurando una reacción ref
usando una definición de función en línea
render = () => {
return (
<div className="drawer" ref={drawer => this.drawerRef = drawer}>
entonces en componentDidMount
la referencia DOM no se establece
componentDidMount = () => {
// this.drawerRef is not defined
Tengo entendido que la ref
devolución de llamada debe ejecutarse durante el montaje, sin embargo, agregar console.log
declaraciones revela componentDidMount
se llama antes de la función de devolución de llamada ref.
Otras muestras de código que he visto, por ejemplo, esta discusión en github indican la misma suposición, se componentDidMount
debe llamar después de cualquier ref
devolución de llamada definida en render
, incluso se indica en la conversación
Entonces, ¿componentDidMount se dispara después de que se hayan ejecutado todas las devoluciones de llamada de referencia?
Si.
Estoy usando react 15.4.1
Algo más que he probado
Para verificar ref
que se estaba llamando a la función, intenté definirla en la clase como tal
setDrawerRef = (drawer) => {
this.drawerRef = drawer;
}
luego en render
<div className="drawer" ref={this.setDrawerRef}>
El registro de la consola en este caso revela que la devolución de llamada se está llamando después componentDidMount
fuente
this
ámbito léxico fuera de su clase. Intente deshacerse de la sintaxis de la función de flecha para sus métodos de clase y vea si ayuda.render
y, por lo tanto, necesitábamos aprovecharlocomponentDidUpdate
, yacomponentDidMount
que no es parte del ciclo de vida de actualización . Probablemente no sea su problema, pero pensé que valdría la pena plantearlo como una posible solución.ref callbacks are invoked before componentDidMount or componentDidUpdate lifecycle hooks.
pero esto no parece ser cierto :(ref = {ref => { this.drawerRef = ref }}
2. Incluso las referencias se invocan antes de componentDidMount; Solo se puede acceder a ref después del renderizado inicial cuando se renderiza el div en su caso. Por lo tanto, debe poder acceder a la referencia en el siguiente nivel, es decir, en componentWillReceiveProps usandothis.drawerRef
3. Si intenta acceder antes del montaje inicial, obtendrá solo los valores indefinidos de ref.Respuestas:
Respuesta corta:
React garantiza que las referencias se establezcan antes
componentDidMount
ocomponentDidUpdate
enganches. Pero solo para niños que realmente se renderizaron .componentDidMount() { // can use any refs here } componentDidUpdate() { // can use any refs here } render() { // as long as those refs were rendered! return <div ref={/* ... */} />; }
Tenga en cuenta que esto no significa que "React siempre establece todas las referencias antes de que se ejecuten estos ganchos".
Veamos algunos ejemplos en los que los árbitros no se establecen.
Las referencias no se establecen para elementos que no se renderizaron
React solo llamará a las devoluciones de llamada de referencia para los elementos que realmente devolvió del renderizado .
Esto significa que si su código se parece a
render() { if (this.state.isLoading) { return <h1>Loading</h1>; } return <div ref={this._setRef} />; }
y en un principio
this.state.isLoading
estrue
, usted debe no esperarthis._setRef
a ser llamado antescomponentDidMount
.Esto debería tener sentido: si su primer render regresó
<h1>Loading</h1>
, no hay forma posible de que React sepa que bajo alguna otra condición devuelve algo más que necesita una referencia adjunta. Tampoco hay nada para establecer la referencia: el<div>
elemento no fue creado porque elrender()
método dijo que no debería ser renderizado.Entonces, con este ejemplo, solo
componentDidMount
se disparará. Sin embargo, cuandothis.state.loading
cambie afalse
, veráthis._setRef
adjunto primero y luegocomponentDidUpdate
disparará.Cuidado con otros componentes
Tenga en cuenta que si pasa a los niños con referencias a otros componentes, existe la posibilidad de que estén haciendo algo que evite la representación (y cause el problema).
Por ejemplo, esto:
<MyPanel> <div ref={this.setRef} /> </MyPanel>
no funcionaría si
MyPanel
no incluyeraprops.children
en su salida:function MyPanel(props) { // ignore props.children return <h1>Oops, no refs for you today!</h1>; }
Nuevamente, no es un error: no habría nada para que React establezca la referencia porque el elemento DOM no fue creado .
Las referencias no se establecen antes de los ciclos de vida si se pasan a un anidado
ReactDOM.render()
Al igual que en la sección anterior, si pasa un hijo con una referencia a otro componente, es posible que este componente haga algo que impida adjuntar la referencia a tiempo.
Por ejemplo, tal vez no está devolviendo al hijo de
render()
, sino que está llamandoReactDOM.render()
a un enlace de ciclo de vida. Puedes encontrar un ejemplo de esto aquí . En ese ejemplo, renderizamos:<MyModal> <div ref={this.setRef} /> </MyModal>
Pero
MyModal
realiza unaReactDOM.render()
llamada en sucomponentDidUpdate
método de ciclo de vida:componentDidUpdate() { ReactDOM.render(this.props.children, this.targetEl); } render() { return null; }
Desde React 16, estas llamadas de procesamiento de nivel superior durante un ciclo de vida se retrasarán hasta que los ciclos de vida se hayan ejecutado para todo el árbol . Esto explicaría por qué no ve los árbitros adjuntos a tiempo.
La solución a este problema es utilizar portales en lugar de
ReactDOM.render
llamadas anidadas :render() { return ReactDOM.createPortal(this.props.children, this.targetEl); }
De esta manera, nuestro
<div>
con una referencia se incluye realmente en la salida de render.Entonces, si encuentra este problema, debe verificar que no haya nada entre su componente y la referencia que pueda retrasar la representación de los niños.
No lo use
setState
para almacenar referenciasAsegúrese de que no está usando
setState
para almacenar la referencia en la devolución de llamada de referencia, ya que es asincrónica y antes de que "termine",componentDidMount
se ejecutará primero.¿Sigue siendo un problema?
Si ninguno de los consejos anteriores le ayuda, presente un problema en React y le echaremos un vistazo.
fuente
Una observación diferente del problema.
Me di cuenta de que el problema solo ocurría en el modo de desarrollo. Después de más investigación, descubrí que la desactivación
react-hot-loader
en mi configuración de Webpack previene este problema.estoy usando
Y es una aplicación electrónica.
Mi configuración de desarrollo parcial de 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' ], ... })
Se volvió sospechoso cuando vi que el uso de la función en línea en render () estaba funcionando, pero el uso de un método enlazado fallaba.
Funciona en cualquier caso
class MyComponent { render () { return ( <input ref={(el) => {this.inputField = el}}/> ) } }
Crash con react-hot-loader (la referencia no está definida en componentDidMount)
class MyComponent { constructor (props) { super(props) this.inputRef = this.inputRef.bind(this) } inputRef (input) { this.inputField = input } render () { return ( <input ref={this.inputRef}/> ) } }
Para ser honesto, la recarga en caliente a menudo ha sido problemática para hacerlo "bien". Con las herramientas de desarrollo que se actualizan rápidamente, cada proyecto tiene una configuración diferente. Quizás mi configuración particular podría arreglarse. Te lo haré saber aquí si ese es el caso.
fuente
El problema también puede surgir cuando intentas usar una referencia de un componente desmontado, como usar una referencia en setinterval y no borras el intervalo establecido durante el desmontaje del componente.
componentDidMount(){ interval_holder = setInterval(() => { this.myref = "something";//accessing ref of a component }, 2000); }
intervalo siempre claro como por ejemplo,
componentWillUnmount(){ clearInterval(interval_holder) }
fuente