Jawaban:
Seperti yang disebutkan Tushar, Anda dapat menyimpan div tiruan di bagian bawah obrolan Anda:
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>
);
}
dan kemudian gulir ke sana setiap kali komponen Anda diperbarui (mis. status diperbarui saat pesan baru ditambahkan):
scrollToBottom = () => {
this.messagesEnd.scrollIntoView({ behavior: "smooth" });
}
componentDidMount() {
this.scrollToBottom();
}
componentDidUpdate() {
this.scrollToBottom();
}
Saya menggunakan metode Element.scrollIntoView standar di sini.
this.messagesEnd.scrollIntoView()
bekerja dengan baik untuk saya. Tidak perlu menggunakan findDOMNode()
.
scrollToBottom(){this.scrollBottom.scrollIntoView({ behavior: 'smooth' })}
untuk membuatnya bekerja dalam versi yang lebih baru
Saya hanya ingin memperbarui jawaban agar sesuai dengan React.createRef()
metode baru , tetapi pada dasarnya sama, hanya perlu diingat current
properti di ref yang dibuat:
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>
)
}
}
MEMPERBARUI:
Sekarang hook tersedia, saya memperbarui jawaban untuk menambahkan penggunaan useRef
dan useEffect
hook, hal yang sebenarnya melakukan keajaiban (React refs dan scrollIntoView
metode DOM) tetap sama:
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>
)
}
Juga buat kotak kode (sangat dasar) jika Anda ingin memeriksa perilaku https://codesandbox.io/s/scrolltobottomexample-f90lz
this.messagesEnd.current
selalu ada. Meskipun demikian, penting untuk diperhatikan bahwa memanggil this.messagesEnd.current
sebelum render pertama akan menghasilkan kesalahan yang Anda tunjuk. Thnx.
this.messagesEnd
contoh pertama Anda dalam metode scrollTo?
useEffect
Metode perlu ditempatkan dengan () => {scrollToBottom()}
. Terima kasih banyak
Jangan gunakan 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
(tidak dapat mengedit karena "suntingan minimal harus 6 karakter", huh).
scrollIntoView
dengan smooth
sangat buruk saat ini.
Terima kasih kepada @enlitement
kita harus menghindari penggunaan findDOMNode
, kita dapat menggunakan refs
untuk melacak komponen
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();
}
referensi:
Anda dapat menggunakan ref
s untuk melacak komponen.
Jika Anda mengetahui cara untuk menyetel ref
salah satu komponen individu (yang terakhir), silakan posting!
Inilah yang menurut saya berhasil untuk saya:
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();
}
}
}
react-scrollable-feed secara otomatis menggulir ke bawah ke elemen terbaru jika pengguna sudah berada di bagian bawah bagian yang dapat digulir. Jika tidak, pengguna akan berada di posisi yang sama. Saya rasa ini cukup berguna untuk komponen obrolan :)
Saya pikir jawaban lain di sini akan memaksa gulir setiap kali tidak peduli di mana bilah gulir itu. Masalah lainnya scrollIntoView
adalah ia akan menggulir seluruh halaman jika div yang dapat digulir tidak terlihat.
Ini bisa digunakan seperti ini:
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>
);
}
}
Pastikan untuk memiliki komponen pembungkus dengan height
ataumax-height
Penafian: Saya adalah pemilik paket
Referensi wadah pesan Anda.
<div ref={(el) => { this.messagesContainer = el; }}> YOUR MESSAGES </div>
Temukan wadah pesan Anda dan buat scrollTop
atributnya sama scrollHeight
:
scrollToBottom = () => {
const messagesContainer = ReactDOM.findDOMNode(this.messagesContainer);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
};
Bangkitkan metode di atas pada componentDidMount
dan componentDidUpdate
.
componentDidMount() {
this.scrollToBottom();
}
componentDidUpdate() {
this.scrollToBottom();
}
Beginilah cara saya menggunakan ini di kode saya:
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>
);
}
}
Saya membuat elemen kosong di akhir pesan, dan menggulir ke elemen itu. Tidak perlu melacak ref.
Jika Anda ingin melakukan ini dengan React Hooks, metode ini dapat diikuti. Untuk div dummy telah ditempatkan di bagian bawah chat. useRef Hook digunakan di sini.
Referensi 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>
);
}
Versi ReactJS saya: 16.12.0
Struktur HTML di dalam render()
fungsi
render()
return(
<body>
<div ref="messageList">
<div>Message 1</div>
<div>Message 2</div>
<div>Message 3</div>
</div>
</body>
)
)
scrollToBottom()
fungsi yang akan mendapatkan referensi dari elemen tersebut. dan scroll sesuai scrollIntoView()
fungsinya.
scrollToBottom = () => {
const { messageList } = this.refs;
messageList.scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"});
}
dan memanggil fungsi di atas di dalam componentDidMount()
dancomponentDidUpdate()
untuk penjelasan lebih lanjut tentang Element.scrollIntoView()
kunjungi developer.mozilla.org
Contoh Kerja:
Anda dapat menggunakan scrollIntoView
metode DOM untuk membuat komponen terlihat dalam tampilan.
Untuk ini, saat merender komponen, berikan ID referensi untuk elemen DOM menggunakan ref
atribut. Kemudian gunakan metode tersebut scrollIntoView
pada componentDidMount
siklus hidup. Saya hanya meletakkan kode contoh yang berfungsi untuk solusi ini. Berikut ini adalah rendering komponen setiap kali pesan diterima. Anda harus menulis kode / metode untuk merender komponen ini.
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>
);
}
}
Berikut this.props.message.MessageId
adalah ID unik dari pesan obrolan tertentu yang diteruskan sebagaiprops
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>
);
}
}
Saya suka melakukannya dengan cara berikut.
componentDidUpdate(prevProps, prevState){
this.scrollToBottom();
}
scrollToBottom() {
const {thing} = this.refs;
thing.scrollTop = thing.scrollHeight - thing.clientHeight;
}
render(){
return(
<div ref={`thing`}>
<ManyThings things={}>
</div>
)
}
terima kasih 'metakermit' untuk jawaban yang bagus, tapi saya pikir kita bisa membuatnya lebih baik, untuk scroll ke bawah, kita harus menggunakan ini:
scrollToBottom = () => {
this.messagesEnd.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
}
tetapi jika Anda ingin menggulir ke atas, Anda harus menggunakan ini:
scrollToTop = () => {
this.messagesEnd.scrollIntoView({ behavior: "smooth", block: "start", inline: "nearest" });
}
dan kode ini umum:
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>
);
}
Sebagai opsi lain, ada baiknya melihat komponen gulir reaksi .
Menggunakan 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>
)
}
}
Ini adalah bagaimana Anda akan menyelesaikan ini di TypeScript (menggunakan ref ke elemen yang ditargetkan tempat Anda menggulir ke):
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>
}
Untuk informasi lebih lanjut tentang menggunakan ref dengan React dan Typecript, Anda dapat menemukan artikel bagus di sini .
Versi lengkap (Skrip Ketik):
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>'.
dan Type 'HTMLDivElement | null' is not assignable to type 'RefObject<unknown>'. Type 'null' is not assignable to type 'RefObject<unknown>'.
...