6

I get this error: Cannot read property 'getBoundingClientRect' of null. I use this function getBoundingClientRect, because in my project I want to have the following efect: at the moment one element is highlighted, the rest has different styles. The message shows the functions handleScroll. The component in which I use looks like this

class QuestionListItem extends Component {
  constructor() {
    super();
    this.state = {
      isActive: false,
    };

    this.handleScroll = this.handleScroll.bind(this);
  }

  componentDidMount = () => {
    window.addEventListener('scroll', this.handleScroll);
    this.handleScroll();
  };

  handleScroll = () => {
    const { isActive } = this.state;
    const { top } = this.wrapRef.getBoundingClientRect();
    if (top > 60 && top < 400 && !isActive) {
      this.setState({ isActive: true });
    }
    if ((top <= 60 || top >= 400) && isActive) {
      this.setState({ isActive: false });
    }
  }

  setWrapRef = (ref) => {
    this.wrapRef = ref;
  }

  render() {
    const { isActive } = this.state;
    const { question } = this.props;
    return (
      <div
        className={`Test__questions-item--noactive ${isActive && 'Test__questions-item--active'}`}
        ref={this.setWrapRef}
      >
        <li key={question.id}>
          <p>
            {question.question}
          </p>
          <QuestionAnswerForm name={question.question} />
        </li>
      </div>
    );
  }
}

Why is there such a mistake? Thanks for the help in advance :)

Elder
  • 341
  • 3
  • 8
  • 21
  • 1
    check this question https://stackoverflow.com/questions/29725828/update-style-of-a-component-onscroll-in-react-js – Amir-Mousavi Jan 04 '19 at 15:42

3 Answers3

2

As I noticed your code, there you used arrow function with componentDidMount.

It should be:

componentDidMount() {}

also if you are using arrow function with handleScroll then there is no need to bind in the constructor, try to remove it and then modify the handleScroll as follows:

handleScroll = () => {
    const { isActive } = this.state;
    if (this.wrapRef) {
       const { top } = this.wrapRef.getBoundingClientRect();
       if (top > 60 && top < 400 && !isActive) {
          this.setState({ isActive: true });
       }
       if ((top <= 60 || top >= 400) && isActive) {
          this.setState({ isActive: false });
       }
    }
  }

Also remove function call this.handleScroll() after event listner in the componentDidMount as it is of no use.

Piyush Zalani
  • 3,686
  • 1
  • 13
  • 29
  • there is no error, however the state is not updated, styles do not change. As if the function `handleScroll` was not called – Elder Jan 04 '19 at 15:36
  • @Elder handleScroll only called if there is a scrollbar in your current component, I have no idea of style but there should be fixed height on the div with overflow property so that it has a scrollbar as it is assigned to scroll event listener – Piyush Zalani Jan 04 '19 at 15:48
  • yes its works. I add componentWillUnmount() { window.removeEventListener('scroll', this.handleScroll); } and everything is ok :) – Elder Jan 08 '19 at 11:32
  • @Elder, please accept the answer if that helps you. – Piyush Zalani Jan 08 '19 at 13:22
0

I think the problem is you have not created any ref for wrapRef. as the Error says cannot get the property of null; so the wrapRef itself is null

class QuestionListItem extends Component {
  constructor() {
    super();
    this.state = {
      isActive: false,
    };

    this.handleScroll = this.handleScroll.bind(this);
  }

  componentDidMount = () => {
    window.addEventListener('scroll', this.handleScroll);
    this.handleScroll();
  };

  wrapRef=React.createRef();

  handleScroll = () => {
    const { isActive } = this.state;
    const { top } = this.wrapRef.current.style;
    if (top > 60 && top < 400 && !isActive) {
      this.setState({ isActive: true });
    }
    if ((top <= 60 || top >= 400) && isActive) {
      this.setState({ isActive: false });
    }
  }

  render() {
    const { isActive } = this.state;
    const { question } = this.props;
    return (
      <div
        className={`Test__questions-item--noactive ${isActive && 'Test__questions-item--active'}`}
        ref={this.wrapRef}
      >
        <li key={question.id}>
          <p>
            {question.question}
          </p>
          <QuestionAnswerForm name={question.question} />
        </li>
      </div>
    );
  }
}
Amir-Mousavi
  • 4,273
  • 12
  • 70
  • 123
0

Simply call the foo.getBoundingClientRect() inside a useEffect, which reads it when the component mounts. I was getting the same error.

Francisco Jesus
  • 115
  • 1
  • 10
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 31 '22 at 07:11