如何在反应中滚动到底部?

我想构建一个聊天系统,并在进入窗口和收到新消息时自动滚动到底部。如何在React中自动滚动到容器的底部?

2020/03/12 15:10:30

The easiest and best way I would recommend is.

My ReactJS version: 16.12.0


HTML structure inside render() function

    render()
        return(
            <body>
                <div ref="messageList">
                    <div>Message 1</div>
                    <div>Message 2</div>
                    <div>Message 3</div>
                </div>
            </body>
        )
    )

scrollToBottom() function which will get reference of the element. and scroll according to scrollIntoView() function.

  scrollToBottom = () => {
    const { messageList } = this.refs;
    messageList.scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"});
  }

and call the above function inside componentDidMount() and componentDidUpdate()

for more explanation about Element.scrollIntoView() visit developer.mozilla.org

小胖Itachi西里2020/03/12 15:10:30

I like doing it the following way.

componentDidUpdate(prevProps, prevState){
  this.scrollToBottom();
}

scrollToBottom() {
  const {thing} = this.refs;
  thing.scrollTop = thing.scrollHeight - thing.clientHeight;
}

render(){
  return(
    <div ref={`thing`}>
      <ManyThings things={}>
    </div>
  )
}
卡卡西Tom2020/03/12 15:10:30

As another option it is worth looking at react scroll component.

小红酱2020/03/12 15:10:30

谢谢“ metakermit”的好答案,但是我认为我们可以做得更好一些,滚动到底部,我们应该使用以下代码:

scrollToBottom = () => {
   this.messagesEnd.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
}

but if you want to scroll top, you should use this:

scrollToTop = () => {
   this.messagesEnd.scrollIntoView({ behavior: "smooth", block: "start", inline: "nearest" });
}   

and this codes are common:

componentDidMount() {
  this.scrollToBottom();
}

componentDidUpdate() {
  this.scrollToBottom();
}


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>
  );
}
路易猪猪2020/03/12 15:10:30

工作示例:

您可以使用DOM scrollIntoView方法使组件在视图中可见。

为此,在渲染组件时,只需使用ref属性为DOM元素提供参考ID 然后使用方法scrollIntoViewcomponentDidMount的生命周期。我只是在为该解决方案提供一个有效的示例代码。以下是每次接收到消息时都会呈现的组件。您应该编写用于渲染此组件的代码/方法。

class ChatMessage extends Component {
    scrollToBottom = (ref) => {
        this.refs[ref].scrollIntoView({ behavior: "smooth" });
    }

    componentDidMount() {
        this.scrollToBottom(this.props.message.MessageId);
    }

    render() {
        return(
            <div ref={this.props.message.MessageId}>
                <div>Message content here...</div>
            </div>
        );
    }
}

this.props.message.MessageId是作为传递的特定聊天消息的唯一IDprops

村村AL2020/03/12 15:10:30

我在消息末尾创建了一个空元素,然后滚动到该元素。无需跟踪引用。

老丝村村Stafan2020/03/12 15:10:29
  1. 引用您的消息容器。

    <div ref={(el) => { this.messagesContainer = el; }}> YOUR MESSAGES </div>
    
  2. 找到您的消息容器并将其scrollTop属性设为相等scrollHeight

    scrollToBottom = () => {
        const messagesContainer = ReactDOM.findDOMNode(this.messagesContainer);
        messagesContainer.scrollTop = messagesContainer.scrollHeight;
    };
    
  3. componentDidMount上调用上述方法componentDidUpdate

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

这就是我在代码中使用它的方式:

 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>
        );
    }
}
伽罗小哥2020/03/12 15:10:29

感谢@enlitement

我们应该避免使用findDOMNode,我们可以refs用来跟踪组件

render() {
  ...

  return (
    <div>
      <div
        className="MessageList"
        ref={(div) => {
          this.messageList = div;
        }}
      >
        { messageListContent }
      </div>
    </div>
  );
}



scrollToBottom() {
  const scrollHeight = this.messageList.scrollHeight;
  const height = this.messageList.clientHeight;
  const maxScrollTop = scrollHeight - height;
  this.messageList.scrollTop = maxScrollTop > 0 ? maxScrollTop : 0;
}

componentDidUpdate() {
  this.scrollToBottom();
}

参考:

凯斯丁2020/03/12 15:10:29

正如Tushar所提到的,您可以在聊天的底部保留一个虚拟div:

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>
  );
}

然后在组件更新时滚动到它(即,随着添加新消息而更新状态):

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

componentDidMount() {
  this.scrollToBottom();
}

componentDidUpdate() {
  this.scrollToBottom();
}

我在这里使用标准的Element.scrollIntoView方法。