Comment faire défiler vers le bas de réagir?

Je veux construire un système de chat et faire défiler automatiquement vers le bas en entrant dans la fenêtre et lorsque de nouveaux messages arrivent. Comment faites-vous défiler automatiquement vers le bas d'un conteneur dans React?

45
demandé sur helsont 2016-06-03 20:54:25

12 réponses

Comme Tushar l'a mentionné, vous pouvez garder un div factice au bas de votre 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>
  );
}

Puis faites défiler jusqu'à chaque fois que votre composant est mis à jour (c'est-à-dire que l'état est mis à jour lorsque de nouveaux messages sont ajoutés):

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

componentDidMount() {
  this.scrollToBottom();
}

componentDidUpdate() {
  this.scrollToBottom();
}

J'utilise l'élément standard .scrollIntoView méthode ici.

93
répondu metakermit 2017-12-08 15:20:41

Ne pas utiliser de findDOMNode

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

  componentDidUpdate() {
    this.scrollToBottom();
  }

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

  render() {
    return <div ref={el => { this.el = el; }} />
  }
}
16
répondu tgdn 2018-02-20 12:34:30

J'ai créé un élément vide à la fin des messages et j'ai fait défiler cet élément. Pas besoin de garder une trace des refs.

7
répondu Tushar Agarwal 2016-12-26 11:44:07

Merci à @ enlitement

, Nous devrions éviter d'utiliser findDOMNode, nous pouvons utiliser refs pour garder une trace de composants

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

Référence:

6
répondu jk2K 2017-01-23 14:50:38
  1. Référencez votre conteneur de messages.

    <div ref={(el) => { this.messagesContainer = el; }}> YOUR MESSAGES </div>
    
  2. Trouvez votre conteneur de messages et rendez son attribut scrollTop égal scrollHeight:

    scrollToBottom = () => {
        const messagesContainer = ReactDOM.findDOMNode(this.messagesContainer);
        messagesContainer.scrollTop = messagesContainer.scrollHeight;
    };
    
  3. Évoquer la méthode ci-dessus sur componentDidMount et componentDidUpdate.

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

Voici comment je l'utilise dans mon 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>
        );
    }
}
6
répondu Marcin Rapacz 2017-02-23 13:33:55

Vous pouvez utiliser ref s pour garder une trace des composants.

Si vous connaissez un moyen pour définir la ref d'un composant individuel (le dernier), merci de poster!

Voici ce que j'ai trouvé qui a fonctionné pour moi:

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();
    }
  }
}
5
répondu helsont 2016-06-03 17:54:25

Je veux juste mettre à jour la réponse pour correspondre à la nouvelle React.createRef() méthode, mais c'est fondamentalement la même, il suffit d'avoir à l'esprit la current propriété dans le créé ref:

class Messages extends React.Component {

  messagesEnd = 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.messagesEnd} />
      </div>
    )
  }
}
4
répondu Diego Lara 2018-09-10 22:23:35

Exemple De Travail:

Vous pouvez utiliser le DOM scrollIntoView méthode pour faire un composant visible dans la vue.

Pour cela, lors du rendu du composant, donnez simplement un ID de référence pour L'élément DOM en utilisant l'attribut ref. Ensuite, utilisez la méthode scrollIntoView sur componentDidMount cycle de vie. Je mets juste un exemple de code de travail pour cette solution. Ce qui suit est un composant de rendu chaque fois qu'un message reçu. Vous devez écrire du code / méthodes pour le rendu ce composant.

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

Ici this.props.message.MessageId est L'identifiant unique du message de chat particulier passé comme props

1
répondu Sherin Jose 2017-11-15 05:56:56

Comme une autre option, il vaut la peine de regarderreact scroll component.

0
répondu John 2017-04-05 14:08:23

J'aime le faire de la manière suivante.

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

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

render(){
  return(
    <div ref={`thing`}>
      <ManyThings things={}>
    </div>
  )
}
0
répondu aestrro 2017-04-26 00:23:35
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
répondu Pavan Garre 2018-03-05 15:30:40

Réagir liste déroulante de flux- défile automatiquement vers le dernier élément si l'utilisateur était déjà au fond du défilement de la section. Sinon, il laissera l'utilisateur à la même position. Je pense que c'est assez utile pour les composants de chat:)

Je pense que les autres réponses ici forceront le défilement à chaque fois, peu importe où se trouvait la barre de défilement. L'autre problème avec scrollIntoView est qu'il fera défiler toute la page si votre div défilable n'était pas visible.

Il peut être utilisé comme ceci :

import * as React from 'react'

import ScrollableFeed from 'react-scrollable-feed'

class App extends React.Component {
  render() {
    const messages = ['Item 1', 'Item 2'];

    return (
      <ScrollableFeed>
        {messages.map((message, i) => <div key={i}>{message}</div>)}
      </ScrollableFeed>
    );
  }
}

Assurez-vous Juste d'avoir un composant wrapper avec un height ou max-height

Avertissement: je suis le propriétaire du paquet

0
répondu Gabriel Bourgault 2018-10-05 21:01:22