24 votes

React ref.current is null

Je travaille sur une application d'agenda/calendrier avec une plage horaire variable. Pour afficher une ligne pour l'heure actuelle et des blocs pour les rendez-vous qui ont été pris, je dois calculer combien de pixels correspondent à une minute à l'intérieur de l'intervalle de temps donné.

Ainsi, par exemple : Si l'agenda commence à 7 heures du matin et se termine à 5 heures de l'après-midi, l'amplitude totale est de 10 heures. Disons que le corps du calendrier a une hauteur de 1000 pixels. Cela signifie que chaque heure représente 100 pixels et chaque minute 1,66 pixels.

Si l'heure actuelle est 3 heures de l'après-midi. Nous sommes à 480 minutes du début de l'ordre du jour. Cela signifie que la ligne indiquant l'heure actuelle doit se trouver à 796,8 pixels (480 * 1,66) du haut du corps du calendrier.

Les calculs ne posent pas de problème, mais la hauteur du corps de l'agenda en pose un. Je pensais utiliser une React Ref pour obtenir la hauteur mais j'obtiens une erreur : ref.current is null

Voici un peu de code :

class Calendar extends Component {
    calendarBodyRef = React.createRef();

    displayCurrentTimeLine = () => {
        const bodyHeight = this.calendarBodyRef.current.clientHeight; // current is null
    }

    render() {
        return (
            <table>
                <thead>{this.displayHeader()}</thead>
                <tbody ref={this.calendarBodyRef}>
                    {this.displayBody()}
                    {this.displayCurrentTimeLine()}
                </tbody>
            </table>
        );
    }
}

22voto

Tom Finney Points 1635

Le problème avec les refs, c'est qu'ils ne sont pas garantis d'être mis en place au premier rendu. Vous pouvez être sûr qu'ils sont mis en place pendant et après componentDidMount Il y a donc deux possibilités pour aller de l'avant.

Vous pourriez utiliser le style callback ref et définir l'état en fonction de celui-ci. Par exemple, au lieu de passer votre ref en tant que prop, vous pouvez passer une référence à une fonction comme this.handleRef et il y aurait un peu de logique là-dedans :

  handleRef = r => {
    this.setState({ bodyHeight: r.clientHeight})
    this.calendarBodyRef .current = r;
  };

Vous pouvez également conserver votre installation actuelle, mais vous devrez déplacer votre clientHeight à une fonction du cycle de vie comme :

  componentDidMount() {
    this.setState({ bodyHeight: this.calendarBodyRef .current.clientHeight });
  }

En fin de compte, vous ne pouvez pas lire immédiatement la valeur actuelle d'un ref comme celui-ci, vous devez le vérifier après le rendu et ensuite lire votre bodyHeight de l'État.

2voto

Lukas Magerusan Points 21

Vous pouvez utiliser une fonction de rappel (ref callback). Dans ce cas, vous n'auriez pas besoin d'utiliser "React-createRef()".

<tbody ref={this.calendarBodyRef}>
...
calendarBodyRef = (e) => {
console.log(e)
}

Vous récupérerez l'élément DOM et n'aurez donc pas besoin d'utiliser "current".

0voto

Dacre Denny Points 17294

S'il est préférable d'éviter le stockage de la taille calculée dans l'état des composants, une autre approche consisterait à introduire un deuxième élément d'information sur la taille du corps. ref (ie elementDisplayHeightRef ) comme suit :

class Calendar extends React.Component {

    /* Create a ref for the body */
    calendarBodyRef = React.createRef();

    /* Create a ref for element where height will be displayed */
    elementDisplayHeightRef = React.createRef();

    displayCurrentTimeLine = () => {

        /* Calculate body height from ref */
        const bodyHeight = this.calendarBodyRef.current.clientHeight;    

        /* Update display */
        this.elementDisplayHeightRef.current.innerText = `bodyHeight:${bodyHeight}`
    }

    render() {
        return (
            <table>
                <thead></thead>
                <tbody ref={this.calendarBodyRef}>
                    <td><td>Some row</td></td>
                    {/* Bind display ref */ }
                    <tr><td ref={this.elementDisplayHeightRef}></td></tr>
                </tbody>
            </table>
        );
    }

    /* Add did mount life cycle hook, and trigger display of body height */
    componentDidMount() {

      this.displayCurrentTimeLine()
    }
}

Cette approche appelle displayCurrentTimeLine() au cours de la componentDidMount() (qui est lui-même appelé après le premier cycle de vie). render() ) pour s'assurer que les deux refs sont entièrement initialisés, avant que la logique du composant n'interagisse avec eux dans la section displayCurrentTimeLine() .

J'espère que cela vous aidera !

0voto

Uddesh_jain Points 1

Si vous utilisez react-redux et enveloppé votre composant dans connect vous devez passer le quatrième argument, c'est-à-dire forwardRef, comme suit.

connect(mapStateToProps, mapDispatchToProps, null, {forwardRef: true})

J'espère que cela vous sera utile.

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:

X