Відповіді:
Як згадував Тушар, ви можете зберігати манекен, який розкривається внизу чату:
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 .
this.messagesEnd.scrollIntoView()
добре працював для мене. Користуватися не було потреби findDOMNode()
.
scrollToBottom(){this.scrollBottom.scrollIntoView({ behavior: 'smooth' })}
змусити її працювати в новіших версіях
Я просто хочу оновити відповідь, щоб вона відповідала новому React.createRef()
методу , але це в основному те саме, просто майте на увазі current
властивість у створеному реф.
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>
)
}
}
ОНОВЛЕННЯ:
Тепер, коли гачки доступні, я оновлюю відповідь, щоб додати використання useRef
та useEffect
гачки, реальна річ, що робить магію (реагувати реф. І 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={messagesEndRef} />
</div>
)
}
Також зробив (дуже основні) кодиandbox, якщо ви хочете перевірити поведінку https://codesandbox.io/s/scrolltobottomexample-f90lz
this.messagesEnd.current
існує завжди. Однак важливо зауважити, що дзвінок this.messagesEnd.current
перед першою візуалізацією призведе до вказаної вами помилки. Thnx.
this.messagesEnd
у вашому першому прикладі методу scrollTo?
useEffect
Потреба метод , який буде розміщений з () => {scrollToBottom()}
. Все одно дуже дякую
Не використовувати 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
(не можна редагувати, оскільки "правки повинні бути не менше 6 символів", зітхають).
scrollIntoView
з на smooth
сьогодні дуже бідна.
Завдяки @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();
}
довідка:
Ви можете використовувати ref
s для відстеження компонентів.
Якщо ви знаєте спосіб встановити 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();
}
}
}
Посилайтеся на контейнер для повідомлень.
<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>
);
}
}
feed-reakct-feed автоматично прокручується до останнього елемента, якщо користувач уже був у нижній частині секції, що прокручується. В іншому випадку він залишить користувача в тому самому положенні. Я думаю, що це дуже корисно для компонентів чату :)
Я думаю, що інші відповіді тут змусять прокручувати щоразу, незалежно від того, де була смуга прокрутки. Інша проблема scrollIntoView
полягає в тому, що вона буде прокручувати всю сторінку, якщо вашого прокручуваного дива не було в режимі перегляду.
Його можна використовувати так:
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
Відмова: Я є власником пакету
Я створив порожній елемент у кінці повідомлень і прокрутив до цього елемента. Немає необхідності слідкувати за реф.
Якщо ви хочете зробити це за допомогою React Hooks, цього методу можна дотримуватися. Для манекена діва розміщено внизу чату. useRef Hook використовується тут.
Довідка API Hooks: 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>
);
}
Моя версія ReactJS: 16.12.0
Структура HTML всередині render()
функції
render()
return(
<body>
<div ref="messageList">
<div>Message 1</div>
<div>Message 2</div>
<div>Message 3</div>
</div>
</body>
)
)
scrollToBottom()
функція, яка отримає посилання на елемент. і прокручуйте відповідно до scrollIntoView()
функції.
scrollToBottom = () => {
const { messageList } = this.refs;
messageList.scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"});
}
і викликати вищевказану функцію всередині componentDidMount()
іcomponentDidUpdate()
для отримання додаткових пояснень про Element.scrollIntoView()
відвідування developer.mozilla.org
Приклад роботи:
Ви можете використовувати scrollIntoView
метод DOM, щоб зробити компонент видимим у поданні.
Для цього під час візуалізації компонента просто вказуйте ідентифікаційний номер для елемента 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
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>
);
}
}
Мені подобається робити це наступним чином.
componentDidUpdate(prevProps, prevState){
this.scrollToBottom();
}
scrollToBottom() {
const {thing} = this.refs;
thing.scrollTop = thing.scrollHeight - thing.clientHeight;
}
render(){
return(
<div ref={`thing`}>
<ManyThings things={}>
</div>
)
}
дякую "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>
);
}
Як інший варіант варто переглянути компонент прокрутки реакції .
Використання 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>
)
}
}
Ось як би ви вирішили це в TypeScript (використовуючи посилання на цільовий елемент, де ви прокручуєте):
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>
}
Для отримання додаткової інформації про використання ref за допомогою React та Typescript ви можете знайти чудову статтю тут .
Повна версія (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>
)
}
}
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>'.
так ...