cameraguy258
cameraguy258

Reputation: 689

React - get element.getBoundingClientRect() after window resize

I have a class that needs to get the size of a DOM element. It works well, but when I resize the window it doesn't update until I change the state in my app, forcing a rerender. I've tried adding this.forceUpdate to a 'resize' event listener in componentDidMount(), but it didn't work. Perhaps I did something wrong? Ideally I'd like to avoid using this.forceUpdate for perfomance implications anyway. Any work arounds for this? Thanks in advance!

My code:

class MyComponent extends React.Component {
  state = { x: 0, y: 0 }

  refCallback = (element) => {
    if (!element) {
      return
    }
    const { x, y } = element.getBoundingClientRect()
    this.setState({ x, y })
  }

  render() {
    console.log('STATE:', this.state) // Outputs the correct x and y values.
    return (
      <div ref={this.refCallback}>
        <button>Hello world</button>
      </div>
    )
  }
}

Upvotes: 8

Views: 12532

Answers (2)

Muhammad Zeeshan
Muhammad Zeeshan

Reputation: 4748

That happening because:

From the React documentation:

Adding a Ref to a DOM Element

React supports a special attribute that you can attach to any component. The ref attribute takes a callback function, and the callback will be executed immediately after the component is mounted or unmounted.

React will call the ref callback with the DOM element when the component mounts, and call it with null when it unmounts.

So, that's why when you refresh you get the value. To overcome the problem you can do something like this:

import React from "react";

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
    this.state = {
      x: 0,
      y: 0
    };
  }

  updateDimensions = () => {
    if (this.myRef.current) {
      const {x, y} = this.myRef.current.getBoundingClientRect();
      this.setState({ x, y });
    }
  };
  componentDidMount() {
    window.addEventListener("resize", this.updateDimensions);
  }
  componentWillUnmount() {
    window.removeEventListener("resize", this.updateDimensions);
  }

  render() {
    console.log("STATE:", this.state); // Outputs the correct x and y values.
    return (
      <div ref={this.myRef}>
        <button>Hello world</button>
      </div>
    );
  }
}

export default MyComponent;

Hope this works for you.

Upvotes: 1

rossipedia
rossipedia

Reputation: 59367

If you want to measure some element in your component whenever the window resizes, it's going to look something like this:

class MyComponent extends React.Component {
  state = {
    x: 0,
    y: 0,
  };

  element = React.createRef();

  onWindowResize = () => {
    if (this.element.current) {
      const {x, y} = this.element.current.getBoundingClientRect();
      this.setState({x, y}, () => {
        console.log(this.state);
      });
    }
  };

  componentDidMount() {
    window.addEventListener('resize', this.onWindowResize);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.onWindowResize);
  }

  render() {
    return (
      <div ref={this.element}>
        <button>Hello, World</button>
      </div>
    );
  }
}

The trick here is that your ref callback is only called once, when the element is initially added to the DOM. If you want to update state whenever you resize the window, you're going to need a 'resize' event handler.

Upvotes: 9

Related Questions