Home > Net >  React.createRef() current is always null
React.createRef() current is always null

Time:12-30

Like many others before me (I have read the related posts and found no answers) I am doing something wrong in this code:

import React, { Component } from 'react';

export class InfoPaneArrow extends Component<InfoPaneArrowProps> {
  infoPaneRef: React.RefObject<HTMLDivElement>;

  positionRef: React.RefObject<HTMLDivElement>;

  constructor(props: InfoPaneArrowProps) {
    super(props);
    this.positionRef = React.createRef();
    this.infoPaneRef = React.createRef();
  }

  render() {
    const markerBox = this.positionRef?.current?.getBoundingClientRect();
    const paneBox = this.infoPaneRef?.current?.getBoundingClientRect();

    const outerClass = { /* other code using markerBox & paneBox positions */ };
    const innerClass = { /* you get the idea */ };
    const arrowClass = { /* ... */ };
    const arrowStyle = { /* ... */ };

    return (
      <div
        className={outerClass}
        ref={this.positionRef}
      >
        <div
          className={innerClass}
          ref={this.infoPaneRef}
        >
          {this.props.children}
          <div
            className={arrowClass}
            style={arrowStyle}
          />
        </div>
      </div>
    );
  }
}

I think my problem might be that I'm using the refs before the render is complete, because the infoPaneRef.current and positionRef.current are always null - but I need the coordinates of the divs (and the window) to do some math and determine whether to reposition the div or not (so it stays always on screen) and which side of the div the arrow should be rendered on (after repositioning).

Any idea what I'm doing wrong here and how to correct it?

CodePudding user response:

I think your guess is correct. During the mounting (first render) ref.current is null. You need to execute your logic on next rendering cycle after the component was mounted. In order to achieve this you can put markerBox and paneBox inside a state, set them in componentDidMount method and make your logic dependent on these state properties.

Inside a constructor add:

this.state = {markerBox: undefined, paneBox: undefined};

Add method to the class:

componentDidMount() {
    const markerBox = this.positionRef?.current?.getBoundingClientRect();
    const paneBox = this.infoPaneRef?.current?.getBoundingClientRect();
    this.setState({markerBox, paneBox});
}

Don't forget to remove from the render method definitions of markerBox and paneBox variables and use state properties via this.state.markerBox and this.state.paneBox.

  • Related