How to scroll to bottom in react?

前端 未结 19 809
执笔经年
执笔经年 2020-11-28 18:27

I want to build a chat system and automatically scroll to the bottom when entering the window and when new messages come in. How do you automatically scroll to the bottom of

相关标签:
19条回答
  • 2020-11-28 19:03

    As Tushar mentioned, you can keep a dummy div at the bottom of your chat:

    render () {
      return (
        <div>
          <div className="MessageContainer" >
            <div className="MessagesList">
              {this.renderMessages()}
            </div>
            <div style={{ float:"left", clear: "both" }}
                 ref={(el) => { this.messagesEnd = el; }}>
            </div>
          </div>
        </div>
      );
    }
    

    and then scroll to it whenever your component is updated (i.e. state updated as new messages are added):

    scrollToBottom = () => {
      this.messagesEnd.scrollIntoView({ behavior: "smooth" });
    }
    
    componentDidMount() {
      this.scrollToBottom();
    }
    
    componentDidUpdate() {
      this.scrollToBottom();
    }
    

    I'm using the standard Element.scrollIntoView method here.

    0 讨论(0)
  • 2020-11-28 19:04

    Full version (Typescript):

    import * as React from 'react'
    
    export class DivWithScrollHere extends React.Component<any, any> {
    
      loading:any = React.createRef();
    
      componentDidMount() {
        this.loading.scrollIntoView(false);
      }
    
      render() {
    
        return (
          <div ref={e => { this.loading = e; }}> <LoadingTile /> </div>
        )
      }
    }

    0 讨论(0)
  • 2020-11-28 19:05
    import React, {Component} from 'react';
    
    export default class ChatOutPut extends Component {
    
        constructor(props) {
            super(props);
            this.state = {
                messages: props.chatmessages
            };
        }
        componentDidUpdate = (previousProps, previousState) => {
            if (this.refs.chatoutput != null) {
                this.refs.chatoutput.scrollTop = this.refs.chatoutput.scrollHeight;
            }
        }
        renderMessage(data) {
            return (
                <div key={data.key}>
                    {data.message}
                </div>
            );
        }
        render() {
            return (
                <div ref='chatoutput' className={classes.chatoutputcontainer}>
                    {this.state.messages.map(this.renderMessage, this)}
                </div>
            );
        }
    }
    
    0 讨论(0)
  • 2020-11-28 19:09

    I just want to update the answer to match the new React.createRef() method, but it's basically the same, just have in mind the current property in the created ref:

    class Messages extends React.Component {
    
      const messagesEndRef = React.createRef()
    
      componentDidMount () {
        this.scrollToBottom()
      }
      componentDidUpdate () {
        this.scrollToBottom()
      }
      scrollToBottom = () => {
        this.messagesEnd.current.scrollIntoView({ behavior: 'smooth' })
      }
      render () {
        const { messages } = this.props
        return (
          <div>
            {messages.map(message => <Message key={message.id} {...message} />)}
            <div ref={this.messagesEndRef} />
          </div>
        )
      }
    }
    

    UPDATE:

    Now that hooks are available, I'm updating the answer to add the use of the useRef and useEffect hooks, the real thing doing the magic (React refs and scrollIntoView DOM method) remains the same:

    import React, { useEffect, useRef } from 'react'
    
    const Messages = ({ messages }) => {
    
      const messagesEndRef = useRef(null)
    
      const scrollToBottom = () => {
        messagesEndRef.current.scrollIntoView({ behavior: "smooth" })
      }
    
      useEffect(scrollToBottom, [messages]);
    
      return (
        <div>
          {messages.map(message => <Message key={message.id} {...message} />)}
          <div ref={messagesEndRef} />
        </div>
      )
    }
    

    Also made a (very basic) codesandbox if you wanna check the behaviour https://codesandbox.io/s/scrolltobottomexample-f90lz

    0 讨论(0)
  • 2020-11-28 19:09

    You can use refs to keep track of the components.

    If you know of a way to set the ref of one individual component (the last one), please post!

    Here's what I found worked for me:

    class ChatContainer extends React.Component {
      render() {
        const {
          messages
        } = this.props;
    
        var messageBubbles = messages.map((message, idx) => (
          <MessageBubble
            key={message.id}
            message={message.body}
            ref={(ref) => this['_div' + idx] = ref}
          />
        ));
    
        return (
          <div>
            {messageBubbles}
          </div>
        );
      }
    
      componentDidMount() {
        this.handleResize();
    
        // Scroll to the bottom on initialization
        var len = this.props.messages.length - 1;
        const node = ReactDOM.findDOMNode(this['_div' + len]);
        if (node) {
          node.scrollIntoView();
        }
      }
    
      componentDidUpdate() {
        // Scroll as new elements come along
        var len = this.props.messages.length - 1;
        const node = ReactDOM.findDOMNode(this['_div' + len]);
        if (node) {
          node.scrollIntoView();
        }
      }
    }
    
    0 讨论(0)
  • 2020-11-28 19:10
    1. Reference your messages container.

      <div ref={(el) => { this.messagesContainer = el; }}> YOUR MESSAGES </div>
      
    2. Find your messages container and make its scrollTop attribute equal scrollHeight:

      scrollToBottom = () => {
          const messagesContainer = ReactDOM.findDOMNode(this.messagesContainer);
          messagesContainer.scrollTop = messagesContainer.scrollHeight;
      };
      
    3. Evoke above method on componentDidMount and componentDidUpdate.

      componentDidMount() {
           this.scrollToBottom();
      }
      
      componentDidUpdate() {
           this.scrollToBottom();
      }
      

    This is how I am using this in my code:

     export default class StoryView extends Component {
    
        constructor(props) {
            super(props);
            this.scrollToBottom = this.scrollToBottom.bind(this);
        }
    
        scrollToBottom = () => {
            const messagesContainer = ReactDOM.findDOMNode(this.messagesContainer);
            messagesContainer.scrollTop = messagesContainer.scrollHeight;
        };
    
        componentDidMount() {
            this.scrollToBottom();
        }
    
        componentDidUpdate() {
            this.scrollToBottom();
        }
    
        render() {
            return (
                <div>
                    <Grid className="storyView">
                        <Row>
                            <div className="codeView">
                                <Col md={8} mdOffset={2}>
                                    <div ref={(el) => { this.messagesContainer = el; }} 
                                         className="chat">
                                        {
                                            this.props.messages.map(function (message, i) {
                                                return (
                                                    <div key={i}>
                                                        <div className="bubble" >
                                                            {message.body}
                                                        </div>
                                                    </div>
                                                );
                                            }, this)
                                        }
                                    </div>
                                </Col>
                            </div>
                        </Row>
                    </Grid>
                </div>
            );
        }
    }
    
    0 讨论(0)
提交回复
热议问题