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


当前回答

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

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

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

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

其他回答

这是你在TypeScript中解决这个问题的方法(使用你滚动到的目标元素的ref):

class Chat extends Component <TextChatPropsType, TextChatStateType> {
  private scrollTarget = React.createRef<HTMLDivElement>();
  componentDidMount() {
    this.scrollToBottom();//scroll to bottom on mount
  }

  componentDidUpdate() {
    this.scrollToBottom();//scroll to bottom when new message was added
  }

  scrollToBottom = () => {
    const node: HTMLDivElement | null = this.scrollTarget.current; //get the element via ref

    if (node) { //current ref can be null, so we have to check
        node.scrollIntoView({behavior: 'smooth'}); //scroll to the targeted element
    }
  };

  render <div>
    {message.map((m: Message) => <ChatMessage key={`chat--${m.id}`} message={m}/>}
     <div ref={this.scrollTarget} data-explanation="This is where we scroll to"></div>
   </div>
}

有关React和Typescript中使用ref的更多信息,你可以在这里找到一篇很棒的文章。

不使用findDOMNode

带有ref的类组件

class MyComponent extends Component {
  componentDidMount() {
    this.scrollToBottom();
  }

  componentDidUpdate() {
    this.scrollToBottom();
  }

  scrollToBottom() {
    this.el.scrollIntoView({ behavior: 'smooth' });
  }

  render() {
    return <div ref={el => { this.el = el; }} />
  }
}

带钩子的功能组件:

import React, { useRef, useEffect } from 'react';

const MyComponent = () => {
  const divRef = useRef(null);

  useEffect(() => {
    divRef.current.scrollIntoView({ behavior: 'smooth' });
  });

  return <div ref={divRef} />;
}

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

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

我在mweb/web中遇到过这个问题。所有的解决方案是很好的在这个页面,但所有的解决方案是不工作,而使用android chrome浏览器。 所以对于mweb和web,我得到了一些小的解决方案。

    import { createRef, useEffect } from 'react';
    import { useSelector } from 'react-redux';
    import { AppState } from 'redux/store';
    import Message from '../Message/Message';
    import styles from './MessageList.module.scss';
    
    const MessageList = () => {
      const messagesEndRef: any = createRef();
      const { messages } = useSelector((state: AppState) => state?.video);
      const scrollToBottom = () => {
          //this is not working in mWeb
            // messagesEndRef.current.scrollIntoView({
            //   behavior: 'smooth',
            //   block: 'end',
            //   inline: 'nearest',
            // });
         const scroll =
          messagesEndRef.current.scrollHeight -
           messagesEndRef.current.clientHeight;
         messagesEndRef.current.scrollTo(0, scroll);
      };
    
      useEffect(() => {
        if (messages.length > 3) {
          scrollToBottom();
        }
      }, [messages]);
    
      return (
        <section className={styles.footerTopSection} ref={messagesEndRef} >
          {messages?.map((message: any) => (
            <Message  key={message.id} {...message} />
          ))}
        </section>
      );
    };
    
    export default MessageList;
 const scrollingBottom = () => {
    const e = ref;

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

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

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