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


当前回答

我只是想更新答案以匹配新的React.createRef()方法,但它基本上是相同的,只要记住在创建的ref中的当前属性:

class Messages extends React.Component {

  const messagesEndRef = React.createRef()

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

更新:

现在钩子可用了,我正在更新答案,添加useRef和useEffect钩子的使用,真正做魔法的事情(React refs和scrollIntoView DOM方法)保持不变:

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

还做了一个(非常基本的)代码和框,如果你想检查行为https://codesandbox.io/s/scrolltobottomexample-f90lz

其他回答

为了向下滚动到页面底部,首先我们必须选择位于页面底部的id。然后我们就可以使用文档了。getElementById选择id并使用scrollIntoView()向下滚动。请参考以下代码。

   scrollToBottom= async ()=>{
      document.getElementById('bottomID').scrollIntoView();
    }

谢谢你的回答,但我认为我们可以做得更好, 对于滚动到底部,我们应该使用这个:

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

但如果你想滚动顶部,你应该使用这个:

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

这些代码很常见:

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

感谢@enlitement

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

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

参考:

https://facebook.github.io/react/docs/react-dom.html#finddomnode https://www.pubnub.com/blog/2016-06-28-reactjs-chat-app-infinite-scroll-history-using-redux/

 const scrollingBottom = () => {
    const e = ref;

    e.current?.scrollIntoView({
      behavior: "smooth",
      block: "center",
      inline: "start",
    });
  };

 useEffect(() => {
    scrollingBottom();
  });

<span ref={ref}>{item.body.content}</span>
    

这是Kent C. Dodds教授的useLayoutEffect的一个很好的用例。

https://kentcdodds.com/blog/useeffect-vs-uselayouteffect

如果你的效果正在改变DOM(通过一个DOM节点ref),并且DOM突变将改变DOM节点的外观,在它被呈现和你的效果改变它之间,那么你不想使用useEffect。

在我的情况下,我在一个div的底部动态生成元素,所以我必须添加一个小超时。

   const bottomRef = useRef<null | HTMLDivElement>(null);

    useLayoutEffect(() => {
        setTimeout(function () {
            if (bottomRef.current) bottomRef.current.scrollTop = bottomRef.current.scrollHeight;
        }, 10);
    }, [transactionsAmount]);