React JS scrollIntoView() not scrolling all the way down

Viewed 5675

I have a small starter React JS project I'm working on where a user types text into a field, the text appears above the field in a fixed height box, and the content in that box scrolls down so that the last element added is always visible. (Essentially like a chat box.)

I've attempted to do this using .scrollIntoView(), but for some reason it doesn't scroll all the way to the bottom of the box. Instead it always stays a few pixels from the bottom. (Firefox 88 on macOS Big Sur)

I have a very stripped down demo of what's happening here: https://ui7bx.csb.app/

You can view the source code below and edit it here: https://codesandbox.io/s/tender-dawn-ui7bx?file=/src/index.js:0-1383

I'm baffled as to what could be going wrong here. Any help would be appreciated.

import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";

class Blog extends React.Component {
  constructor(props) {
    super(props);
    this.sendMsg = this.sendMsg.bind(this);
    this.textInput = React.createRef();
    this.state = {
      posts: [
        {content: "This is decent sized post"},
        {content:"Another post that is a little longer and extends the boundaries of space and time"}
      ]
    };
  }
  sendMsg(event) {
    event.preventDefault(); // stop page from refreshing
    // update state with new post
    let posts = this.state.posts;
    const content = this.textInput.current.value; // get field value
    posts.push({content: content});
    this.setState({ posts: posts });

    //scroll down to the bottom
    this.msgEnd.scrollIntoView({behavior: "smooth"});
  }
  render() {
    const msgList = this.state.posts.map((post, index) => {
      return (
        <div className={"postWrapper"} key={index}>{post.content}</div>
      );
    });
    return (
      <div>
        <div id="list">
          {msgList}
          <div ref={el => {this.msgEnd = el; }}></div>
        </div>
        <form id="chatForm">
          <input type="text" ref={this.textInput} />
          <button onClick={this.sendMsg}>Send</button>
        </form>
      </div>
    );
  }
}

ReactDOM.render(<Blog />, document.getElementById("root"));
1 Answers

I think you need the scroll to happen after the posts have been updated, so they are all (including the new one that is added in the sendMsg() method) rendered and the msgEnd ref is up to date.

  componentDidUpdate(prevProps, prevState) {
    //maybe add conditional logic to only scroll when the posts have changed
    this.msgEnd.scrollIntoView({
      block: "nearest",
      inline: "center",
      behavior: "smooth",
      alignToTop: false
    });
  }
Related