Antworten:
Wie Tushar erwähnt hat, können Sie einen Dummy-Div am Ende Ihres Chats behalten:
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>
);
}
und scrollen Sie dann immer dann dorthin, wenn Ihre Komponente aktualisiert wird (dh der Status wird aktualisiert, wenn neue Nachrichten hinzugefügt werden):
scrollToBottom = () => {
this.messagesEnd.scrollIntoView({ behavior: "smooth" });
}
componentDidMount() {
this.scrollToBottom();
}
componentDidUpdate() {
this.scrollToBottom();
}
Ich verwende hier die Standardmethode Element.scrollIntoView .
this.messagesEnd.scrollIntoView()hat gut für mich funktioniert. Es bestand keine Notwendigkeit zu verwenden findDOMNode().
scrollToBottom(){this.scrollBottom.scrollIntoView({ behavior: 'smooth' })}, damit sie in neueren Versionen
Ich möchte nur die Antwort aktualisieren, um sie an die neue React.createRef() Methode anzupassen , aber im Grunde ist es dieselbe. Denken Sie nur an die currentEigenschaft in der erstellten Referenz:
class Messages extends React.Component {
const messagesEndRef = 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.messagesEndRef} />
</div>
)
}
}
AKTUALISIEREN:
Jetzt, da Hooks verfügbar sind, aktualisiere ich die Antwort, um die Verwendung von useRefund hinzuzufügenuseEffect Hooks reale Sache, die die Magie ausführt (React Refs und scrollIntoViewDOM-Methode), bleibt dieselbe:
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>
)
}
Außerdem wurde eine (sehr einfache) Codesandbox erstellt, wenn Sie das Verhalten https://codesandbox.io/s/scrolltobottomexample-f90lz überprüfen möchten
this.messagesEnd.currentimmer vorhanden. Es ist jedoch wichtig zu beachten, dass ein Aufruf this.messagesEnd.currentvor dem ersten Rendern zu dem Fehler führt, auf den Sie hingewiesen haben. Danke.
this.messagesEndin Ihrem ersten Beispiel in der scrollTo-Methode?
useEffectMethode muss mit platziert werden () => {scrollToBottom()}. Trotzdem vielen Dank
Verwende nicht 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; }} />
}
}
import React, { useRef, useEffect } from 'react';
const MyComponent = () => {
const divRref = useRef(null);
useEffect(() => {
divRef.current.scrollIntoView({ behavior: 'smooth' });
});
return <div ref={divRef} />;
}
behavior(kann nicht bearbeitet werden, da "Änderungen mindestens 6 Zeichen umfassen müssen", seufz).
scrollIntoViewmit smoothist im Moment sehr schlecht.
Danke an @enlitement
Wir sollten die Verwendung vermeiden findDOMNode, wir können verwenden refs, um die Komponenten im Auge zu behalten
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();
}
Referenz:
Sie können verwenden ref s die Komponenten verfolgen.
Wenn Sie wissen, wie Sie refeine einzelne Komponente (die letzte) einstellen können , posten Sie bitte!
Folgendes hat für mich funktioniert:
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();
}
}
}
Verweisen Sie auf Ihren Nachrichtencontainer.
<div ref={(el) => { this.messagesContainer = el; }}> YOUR MESSAGES </div>Suchen Sie Ihren Nachrichtencontainer und machen Sie sein scrollTopAttribut gleich scrollHeight:
scrollToBottom = () => {
const messagesContainer = ReactDOM.findDOMNode(this.messagesContainer);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
};Rufen Sie die obige Methode auf componentDidMountund auf componentDidUpdate.
componentDidMount() {
this.scrollToBottom();
}
componentDidUpdate() {
this.scrollToBottom();
}So verwende ich das in meinem 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>
);
}
}
React-Scrollable-Feed scrollt automatisch zum neuesten Element, wenn sich der Benutzer bereits am unteren Rand des scrollbaren Abschnitts befand. Andernfalls bleibt der Benutzer an derselben Position. Ich denke, das ist ziemlich nützlich für Chat-Komponenten :)
Ich denke, die anderen Antworten hier erzwingen jedes Mal einen Bildlauf, egal wo sich die Bildlaufleiste befand. Das andere Problem scrollIntoViewist, dass die gesamte Seite gescrollt wird, wenn Ihr scrollbares Div nicht angezeigt wurde.
Es kann folgendermaßen verwendet werden:
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>
);
}
}
Stellen Sie einfach sicher, dass Sie eine Wrapper-Komponente mit einem bestimmten heightoder habenmax-height
Haftungsausschluss: Ich bin der Eigentümer des Pakets
Ich habe am Ende von Nachrichten ein leeres Element erstellt und zu diesem Element gescrollt. Keine Notwendigkeit, die Refs im Auge zu behalten.
Wenn Sie dies mit React Hooks tun möchten, können Sie dieser Methode folgen. Für einen Dummy wurde div am Ende des Chats platziert. Hier wird useRef Hook verwendet.
Hooks-API-Referenz: https://reactjs.org/docs/hooks-reference.html#useref
import React, { useEffect, useRef } from 'react';
const ChatView = ({ ...props }) => {
const el = useRef(null);
useEffect(() => {
el.current.scrollIntoView({ block: 'end', behavior: 'smooth' });
});
return (
<div>
<div className="MessageContainer" >
<div className="MessagesList">
{this.renderMessages()}
</div>
<div id={'el'} ref={el}>
</div>
</div>
</div>
);
}
Meine ReactJS-Version: 16.12.0
HTML-Struktur innerhalb der render()Funktion
render()
return(
<body>
<div ref="messageList">
<div>Message 1</div>
<div>Message 2</div>
<div>Message 3</div>
</div>
</body>
)
)
scrollToBottom()Funktion, die die Referenz des Elements erhält. und scrollen Sie nach scrollIntoView()Funktion.
scrollToBottom = () => {
const { messageList } = this.refs;
messageList.scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"});
}
und rufen Sie die obige Funktion in componentDidMount()und aufcomponentDidUpdate()
Weitere Erklärungen finden Element.scrollIntoView()Sie unter developer.mozilla.org
Arbeitsbeispiel:
Mit der DOM- scrollIntoViewMethode können Sie eine Komponente in der Ansicht sichtbar machen.
Geben Sie dazu beim Rendern der Komponente lediglich eine Referenz-ID für das DOM-Element mithilfe des refAttributs an. Verwenden Sie dann die Methode scrollIntoViewfür den componentDidMountLebenszyklus. Ich stelle gerade einen funktionierenden Beispielcode für diese Lösung ein. Das Folgende ist eine Komponente, die jedes Mal gerendert wird, wenn eine Nachricht empfangen wird. Sie sollten Code / Methoden zum Rendern dieser Komponente schreiben.
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>
);
}
}
Hier this.props.message.MessageIdist die eindeutige ID der jeweiligen Chat-Nachricht, die als übergeben wurdeprops
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>
);
}
}
danke 'metakermit' für seine gute antwort, aber ich denke wir können es ein bisschen besser machen, für scrollen nach unten sollten wir folgendes verwenden:
scrollToBottom = () => {
this.messagesEnd.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
}
Wenn Sie jedoch nach oben scrollen möchten, sollten Sie Folgendes verwenden:
scrollToTop = () => {
this.messagesEnd.scrollIntoView({ behavior: "smooth", block: "start", inline: "nearest" });
}
und diese Codes sind üblich:
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>
);
}
Als weitere Option lohnt es sich, die React Scroll- Komponente zu betrachten.
Verwenden von React.createRef()
class MessageBox extends Component {
constructor(props) {
super(props)
this.boxRef = React.createRef()
}
scrollToBottom = () => {
this.boxRef.current.scrollTop = this.boxRef.current.scrollHeight
}
componentDidUpdate = () => {
this.scrollToBottom()
}
render() {
return (
<div ref={this.boxRef}></div>
)
}
}
So würden Sie dies in TypeScript lösen (indem Sie den Verweis auf ein Zielelement verwenden, zu dem Sie scrollen):
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>
}
Weitere Informationen zur Verwendung von ref mit React und Typescript finden Sie hier .
Vollversion (Typoskript):
import * as React from 'react'
export class DivWithScrollHere extends React.Component<any, any> {
loading:any = React.createRef();
componentDidMount() {
this.loading.scrollIntoView(false);
}
render() {
return (
<div ref={e => { this.loading = e; }}> <LoadingTile /> </div>
)
}
}
Property 'scrollIntoView' does not exist on type 'RefObject<unknown>'.Type 'HTMLDivElement | null' is not assignable to type 'RefObject<unknown>'. Type 'null' is not assignable to type 'RefObject<unknown>'.