Відповіді:
Як згадував Тушар, ви можете зберігати манекен, який розкривається внизу чату:
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();
}
довідка:
Ви можете використовувати refs для відстеження компонентів.
Якщо ви знаєте спосіб встановити 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>'. так ...