Я хочу создать чат-систему и автоматически прокручивать ее донизу при входе в окно и при появлении новых сообщений. Как вы автоматически прокручиваете нижнюю часть контейнера в React?
Как прокручивать донизу в ответ?
Ответ 1
Как упоминал Тушар, вы можете оставить фиктивный 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();
}
Я использую стандартный метод Element.scrollIntoView здесь.
Ответ 2
Не использовать 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; }} />
}
}
Ответ 3
Я просто хочу, чтобы обновить ответ, чтобы соответствовать новому React.createRef()
метод, но это в принципе то же самое, только имеет в виде current
свойства в созданном иом:
class Messages extends React.Component {
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>
)
}
}
ОБНОВИТЬ:
Теперь, когда хуки доступны, я обновляю ответ, чтобы добавить использование useRef
и useEffect
Реальная магия (метод React refs и метод scrollIntoView
DOM) остается прежней:
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={this.messagesEndRef} />
</div>
)
}
Также сделал (очень простые) коды и ящик, если вы хотите проверить поведение https://codesandbox.io/s/scrolltobottomexample-f90lz
Ответ 4
Благодаря @enlitement
нам следует избегать использования findDOMNode
, мы можем использовать refs
для отслеживания компонентов
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();
}
ссылка:
Ответ 5
Вы можете использовать ref
для отслеживания компонентов.
Если вы знаете способ установки ref
одного отдельного компонента (последнего), напишите сообщение
Вот что я нашел для меня:
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();
}
}
}
Ответ 6
Я создал пустой элемент в конце сообщений и прокручивал этот элемент. Нет необходимости отслеживать рефери.
Ответ 7
-
Ссылка на контейнер сообщений.
<div ref={(el) => { this.messagesContainer = el; }}> YOUR MESSAGES </div>
-
Найдите свой контейнер сообщений и сделайте его атрибут
scrollTop
равнымscrollHeight
:scrollToBottom = () => { const messagesContainer = ReactDOM.findDOMNode(this.messagesContainer); messagesContainer.scrollTop = messagesContainer.scrollHeight; };
-
Вызвать выше метод на
componentDidMount
иcomponentDidUpdate
.componentDidMount() { this.scrollToBottom(); } componentDidUpdate() { this.scrollToBottom(); }
Вот как я использую это в своем коде:
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>
);
}
}
Ответ 8
Рабочий пример:
Вы можете использовать метод DOM scrollIntoView
, чтобы сделать компонент видимым в представлении.
Для этого при рендеринге компонента просто укажите ссылочный идентификатор для элемента DOM с помощью атрибута ref
. Затем используйте метод scrollIntoView
на componentDidMount
жизненный цикл. Я просто помещаю рабочий пример кода для этого решения. Ниже представлен компонент, каждый раз при получении сообщения. Вы должны написать код/методы для рендеринга этого компонента.
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>
);
}
}
Здесь this.props.message.MessageId
- уникальный идентификатор конкретного сообщения чата, переданного как props
Ответ 9
прокручиваемая передача автоматически прокручивается до последнего элемента, если пользователь уже находится в нижней части прокручиваемой секции. В противном случае он оставит пользователя в том же положении. Я думаю, что это очень полезно для компонентов чата :)
Я думаю, что другие ответы здесь заставят прокручивать каждый раз независимо от того, где была полоса прокрутки. Другая проблема с scrollIntoView
заключается в том, что она будет прокручивать всю страницу, если ваш прокручиваемый div не был в поле зрения.
Его можно использовать следующим образом:
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>
);
}
}
Просто убедитесь, что есть оберточный компонент с определенной height
или max-height
Отказ от ответственности: я являюсь владельцем пакета
Ответ 10
В качестве еще одного варианта стоит посмотреть отредактировать прокрутку.
Ответ 11
Мне нравится делать это следующим образом.
componentDidUpdate(prevProps, prevState){
this.scrollToBottom();
}
scrollToBottom() {
const {thing} = this.refs;
thing.scrollTop = thing.scrollHeight - thing.clientHeight;
}
render(){
return(
<div ref={`thing`}>
<ManyThings things={}>
</div>
)
}
Ответ 12
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>
);
}
}
Ответ 13
Полная версия (Typescript):
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>
)
}
}
Ответ 14
спасибо 'metakermit' за хороший ответ, но я думаю, что мы можем сделать его немного лучше, для прокрутки вниз, мы должны использовать это:
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>
);
}
Ответ 15
Если вы хотите сделать это с помощью React Hooks, этот метод можно использовать. Для фиктивного div был размещен в нижней части чата. useRef Hook используется здесь.
Справочник по API хуков: 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>
);
}
Ответ 16
****Can anyone please let me know why its not working****
<div>
<Row>
<Col >
<div className="CONTAINER">
<A FUNCTIONAL COMPONENT />
<div style={{ float:"left", clear: "both" }} ref={(el) => { this.messagesEnd = el; }}>
</div>
</div>
</Col>
<Col >
<A FUNCTIONAL COMPONENT />
</Col>
</Row>
</div>
scrollToBottom = () => {
this.messagesEnd.scrollIntoView({ behavior: "smooth" });
}
componentDidUpdate() {
this.scrollToBottom(true);
}