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


当前回答

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

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

其他回答

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

我使用的是标准元素。scrollIntoView方法。

您可以使用引用来跟踪组件。

如果你知道如何设置单个组件的引用(最后一个),请发布!

以下是我发现对我有用的方法:

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

这是根据上面的答案修改的,以支持“children”而不是数据数组。

注意:样式化组件的使用对于解决方案并不重要。

import {useEffect, useRef} from "react";
import React from "react";
import styled from "styled-components";

export interface Props {
    children: Array<any> | any,
}

export function AutoScrollList(props: Props) {
    const bottomRef: any = useRef();

    const scrollToBottom = () => {
        bottomRef.current.scrollIntoView({
            behavior: "smooth",
            block: "start",
        });
    };

    useEffect(() => {
        scrollToBottom()
    }, [props.children])

    return (
        <Container {...props}>
            <div key={'child'}>{props.children}</div>
            <div key={'dummy'} ref={bottomRef}/>
        </Container>
    );
}

const Container = styled.div``;

这是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]);

我在消息的末尾创建了一个空元素,并滚动到该元素。不需要跟踪裁判。