61 votes

composantDidMount appelé AVANT le rappel en retour

Problème

Je suis une réagir ref à l'aide d'une fonction inline définition

render = () => {
    return (
        <div className="drawer" ref={drawer => this.drawerRef = drawer}>

puis, en componentDidMount DOM de référence n'est pas définie

componentDidMount = () => {
    // this.drawerRef is not defined

Ma compréhension est l' ref de rappel doit être exécuté lors d'un montage, cependant l'ajout d' console.log des déclarations révèle componentDidMount est appelé avant la ref fonction de rappel.

D'autres exemples de code que j'ai regardé, par exemple cette discussion sur github indiquent la même hypothèse, componentDidMount doit être appelée après tout ref rappels définie en render, c'est même indiqué dans la conversation

Donc componentDidMount est tiré après toutes les ref des rappels ont été exécuté?

Oui.

Je suis l'aide de réagir 15.4.1

Autre chose que j'ai essayé

Pour vérifier l' ref fonction a été appelée, j'ai essayé de le définir sur la classe en tant que telle

setDrawerRef = (drawer) => {
  this.drawerRef = drawer;
}

puis, en render

<div className="drawer" ref={this.setDrawerRef}>

Journalisation de la Console dans ce cas révèle le rappel est en effet appelée après l' componentDidMount

103voto

Dan Points 16670

Réponse courte:

Réagir garantit que les refs sont définies avant l' componentDidMount ou componentDidUpdate des crochets. Mais seulement pour les enfants qui effectivement rendus.

componentDidMount() {
  // can use any refs here
}

componentDidUpdate() {
  // can use any refs here
}

render() {
  // as long as those refs were rendered!
  return <div ref={/* ... */} />;
}

Remarque cela ne veut pas dire "Réagissent toujours ensembles de toutes les refs avant ces crochets exécuter".
Regardons quelques exemples de cas où les arbitres ne pas se fixer.


Refs n'obtenez pas fixé pour les éléments qui n'étaient pas rendus

Réagir appelle uniquement réf rappels pour les éléments que vous avez réellement retourné à partir de rendu.

Cela signifie que si votre code ressemble à

render() {
  if (this.state.isLoading) {
    return <h1>Loading</h1>;
  }

  return <div ref={this._setRef} />;
}

et d'abord, this.state.isLoading est true, vous devriez pas attendre this._setRef à être appelée avant d' componentDidMount.

Cela doit faire sens: si votre premier rendu retourné <h1>Loading</h1>, il n'y a aucun moyen possible pour Réagir à savoir qu'en vertu d'un autre état, il renvoie à quelque chose d'autre qui a besoin d'une ref pour être fixé. Il n'y a également rien à la réf à: l' <div> élément n'a pas été créé à cause de l' render() méthode a dit qu'il ne devrait pas être rendue.

Donc, avec cet exemple, seulement componentDidMount le feu. Cependant, lors de l' this.state.loading des changements d' false, vous verrez this._setRef attaché d'abord, et ensuite, componentDidUpdate le feu.


Regarder dehors pour d'autres composants

Notez que si vous passez des enfants avec des références vers d'autres composants il y a une chance qu'ils font quelque chose qui empêche le rendu (et à l'origine du problème).

Par exemple, ceci:

<MyPanel>
  <div ref={this.setRef} />
</MyPanel>

ne fonctionne pas si MyPanel ne comprennent props.children de sa sortie:

function MyPanel(props) {
  // ignore props.children
  return <h1>Oops, no refs for you today!</h1>;
}

Encore une fois, ce n'est pas un bug: il n'y aurait rien pour Réagir à l'ensemble de la ref, parce que l'élément DOM n'a pas été créé.


Refs n'obtenez pas ensemble avant le cycle de vie s'ils sont transmis à un imbriquée ReactDOM.render()

Similaire à la section précédente, si vous passez un enfant avec une ref à un autre composant, il est possible que ce composant peut faire quelque chose qui empêche la fixation de la ref dans le temps.

Par exemple, c'est peut-être pas le retour de l'enfant à partir de render(), et au lieu de l'appelant ReactDOM.render() dans le cycle de crochet. Vous pouvez trouver un exemple de cela ici. Dans cet exemple, nous avons rendu:

<MyModal>
  <div ref={this.setRef} />
</MyModal>

Mais MyModal effectue une ReactDOM.render() appel dans ses componentDidUpdate du cycle de vie de la méthode:

componentDidUpdate() {
  ReactDOM.render(this.props.children, this.targetEl);
}

render() {
  return null;
}

Depuis Réagir 16, tel haut niveau de rendu des appels au cours d'un cycle de vie sera retardée jusqu'à ce que le cycle de vie ont couru pour l'ensemble de l'arbre. Cela pourrait expliquer pourquoi vous ne voyez pas les refs fixé dans le temps.

La solution à ce problème est l'utilisation de portails au lieu de imbriquée ReactDOM.render appels:

render() {
  return ReactDOM.createPortal(this.props.children, this.targetEl);
}

De cette façon, notre <div> avec une ref est en fait inclus dans la file de sortie.

Donc, si vous rencontrez ce problème, vous devez vérifier il n'y a rien entre le composant et le ref qui pourrait retarder le rendu des enfants.

Ne pas utiliser setState pour stocker les refs

Assurez-vous que vous n'êtes pas à l'aide de setState à la boutique du ref dans la réf de rappel, comme il est asynchrone et avant qu'il est "fini", componentDidMount sera exécuté en premier.


Encore une Question?

Si aucun des conseils au-dessus de l'aide, fichier un problème de Réagir et nous prenons un coup d'oeil.

0voto

Amida Points 517

Une autre observation du problème.

Je me suis rendu compte que la question ne s'est produite alors que le mode de développement. Après une enquête plus approfondie, j'ai trouvé que la désactivation react-hot-loader dans mon Webpack config évite ce problème.

Je suis à l'aide de

  • "réagir à chaud--loader": "3.1.3"
  • "webpack": "4.10.2",

Et c'est un électron app.

Mon partiel Webpack développement config

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'
  ],
  ...
})

Il est devenu suspect quand j'ai vu que l'utilisation de la fonction inline dans render () a été de travail, mais en utilisant une méthode liée a été s'écraser.

Fonctionne en tout cas

class MyComponent {
  render () {
    return (
      <input ref={(el) => {this.inputField = el}}/>
    )
  }
}

Crash à réagir à chaud-chargeur (réf est pas défini dans componentDidMount)

class MyComponent {
  constructor (props) {
    super(props)
    this.inputRef = this.inputRef.bind(this)
  }

  inputRef (input) {
    this.inputField = input
  }

  render () {
    return (
      <input ref={this.inputRef}/>
    )
  }
}

Pour être honnête, chaud recharger a souvent été problématique pour obtenir le "droit". Avec les outils de dev de la mise à jour rapide, chaque projet a une autre config. Peut-être que ma config pourrait être résolu. Je vais vous laisser savoir ici si c'est le cas.

-1voto

Dans composantDidMount, pouvez-vous trouver votre élément ref (div.drawer) dans le DOM du navigateur? Sinon, vous ne pouvez pas avoir sa référence. Étant donné que le problème se trouve dans un autre code plus volumineux, la raison peut être que l’élément ref (div.drawer) n’est pas restitué.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by: