2

I want to manipulate the DOM in ReactJS in the componentDidMount() method. My problem is that at this time the DOM isn't fully rendered somehow and I need a setTimeout function, which I would rather omit.

When I console.log the scrollHeight of the rendered element in componentDidMount() it gives me a different number as when I wait for let's say 100 milliseconds.

What I want to achieve is to scroll down to the end of an element which is described here How to scroll to bottom in react?

The component is a modal-window which renders {this.props.children} of another component. The modal-window is rendered into the DOM with visibility: hidden and opacity: 0 and it has the height of the window, when it first appears on the page. By clicking on a button it shows up and still has the height of the window until I wait some milliseconds.

I guess, I do something wrong here when setTimeout is needed, but I didn't found out what.

I also tried to change the DOM in the componentDidUpdate() method with the same results.

I wrote this code in the modal-window component:

componentDidMount() {
  console.log(document.querySelector('.myModal').scrollHeight);
  setTimeout(function() {
    console.log(document.querySelector('.myModal').scrollHeight);
  }, 100);
}

First console.log gives me for example 497 and the second one something like 952.

Update

I have a modal-window component which renders a child like this for example for my inbox-thread:

<Modal>
  <InboxThread />
</Modal>

The problem was, that I needed to wait until the modal-window component rendered its children like this in the Modal.js:

render() {
    return (
      <React.Fragment>
        {this.props.children}
      </React.Fragment>
    );
  }

So my solution in the end was to hand over a method in the props from the parent component where I call the modal to check if componentDidUpdate() in Modal.js.

My code looks now like this in the parent component:

...
export default class InboxThreadList extends React.Component {
  constructor(props) {
    super(props);
    this.scrollToModalBottom = this.scrollToModalBottom.bind(this);
  }
  render() {
    return (
    <React.Fragment>
      ...
      <Modal onRender={this.scrollToModalBottom}>
        <InboxThread/>
      </Modal>
    </React.Fragment>
    )
  }
  scrollToModalBottom() {
    const myModalObject = document.querySelector('.myModal');
    myModalObject.scrollTop = myModalObject.scrollHeight;
  }
}

And in the Modal.js:

...
export default class Modal extends React.Component {
  ...
  componentDidUpdate() {
    if ('onRender' in this.props) {
      this.props.onRender();
    }
  }
  render() {
    return (
      <div className={'myModal'}>
        {this.props.children}
      </div>
    );
  }

I know! I still should work with refs instead of document.querySelector and I will do as described here React - Passing ref from dumb component(child) to smart component(parent).

Kurt Lagerbier
  • 170
  • 2
  • 11
  • the entire point of componentDidMount() is to make sure the DOM has loaded so you definitely do not want to use setTimeout. It looks like you are using the parents componentDidMount() instead of the Modals? Also you shouldn't access DOM directly use ref's – GifCo May 01 '19 at 16:02

1 Answers1

1

If you use a ref - as long as the element is always rendered in render() - it is guaranteed to resolve before componentDidMount runs:

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

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

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

componentDidMount called BEFORE ref callback

So in your case, you might code it a bit like this:

componentDidMount() {
  console.log(this.mymodal.scrollHeight)
}

render() {
 return <div className="mymodal" ref={ref => this.mymodal = ref} />
}
jsdeveloper
  • 3,945
  • 1
  • 15
  • 14
  • I tried now to use `ref` but with the same result. I guess my problem is the way I use my modal window: ``` ``` I tried to scroll to the bottom in the modal-child InboxThread component. I fill the modal when it gets rendered with content. – Kurt Lagerbier May 01 '19 at 16:45
  • Downvoted because even if the `ref` is defined as suggested it `scrollHeight` is initially 0 in `componentDidMount`. Only with a `setTimeOut` the actual value is returned. – Kees de Kooter May 21 '19 at 08:59