beingalex
beingalex

Reputation: 2476

Keep an element scrolled to the bottom

I am creating a "log" screen that updates periodically in React. To make sure that the latest addition is always in view I would like the element to always be scrolled to the bottom:

Example of what I want to acheive

I have the overflow working:

CSS:

div.log {
    height: 200px;
    overflow: auto;
}

And the HTML:

JSX:

render() {

    return (

            <div className="log">
                <ul>
                    {this.props.log.map((log, i) =>

                        <li key={i}>
                            {log.message}
                        </li>
                    )}

                </ul>
            </div>

    );
}

Here is my entire component:

Javascript

import React from 'react';

export default class Log extends React.Component {

    constructor(props) {

        super(props);

    }

    render() {

        return (

                <div className="log">
                    <ul>
                        {this.props.log.map((log, i) =>

                            <li key={i}>
                                {log.message}
                            </li>
                        )}

                    </ul>
                </div>

        );
    }

}

This all works fine, but doesn't stay scrolled down. I understand that I can use something similar to:

 var element = document.getElementByClassName(".log");
 element.scrollTop = element.scrollHeight;

But this only works once, I need to be scrolled to the bottom when the contents of ".log" updates.

Any help is much appreciated.

Upvotes: 1

Views: 80

Answers (1)

Tholle
Tholle

Reputation: 112777

You can give a ref to the topmost element of the Log component and make sure it is scrolled to the bottom when the props update in componentDidUpdate.

Example

class Log extends React.Component {
  ref = React.createRef();

  componentDidUpdate() {
    this.ref.current.scrollTop = this.ref.current.scrollHeight;
  }

  render() {
    return (
      <div ref={this.ref} style={{ height: 50, overflowY: "scroll" }}>
        <ul>
          {this.props.log.map((log, i) => (
            <li key={i}>{log.message}</li>
          ))}
        </ul>
      </div>
    );
  }
}

class App extends React.Component {
  state = { logs: [] };

  componentDidMount() {
    setInterval(() => {
      this.setState(prevState => {
        return { logs: [...prevState.logs, { message: Math.random() }] };
      });
    }, 1000);
  }

  render() {
    return <Log log={this.state.logs} />;
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>

Upvotes: 2

Related Questions