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?
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.
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; }} />
}
}
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.
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:
-
Référencez votre conteneur de messages.
<div ref={(el) => { this.messagesContainer = el; }}> YOUR MESSAGES </div>
-
Trouvez votre conteneur de messages et rendez son attribut
scrollTop
égalscrollHeight
:scrollToBottom = () => { const messagesContainer = ReactDOM.findDOMNode(this.messagesContainer); messagesContainer.scrollTop = messagesContainer.scrollHeight; };
-
Évoquer la méthode ci-dessus sur
componentDidMount
etcomponentDidUpdate
.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>
);
}
}
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();
}
}
}
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>
)
}
}
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
Comme une autre option, il vaut la peine de regarderreact scroll component.
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>
)
}
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>
);
}
}
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