ฉันต้องการสร้างระบบแชทและเลื่อนไปที่ด้านล่างโดยอัตโนมัติเมื่อเข้าสู่หน้าต่างและเมื่อมีข้อความใหม่เข้ามาคุณจะเลื่อนไปที่ด้านล่างของคอนเทนเนอร์โดยอัตโนมัติใน React ได้อย่างไร?
ฉันต้องการสร้างระบบแชทและเลื่อนไปที่ด้านล่างโดยอัตโนมัติเมื่อเข้าสู่หน้าต่างและเมื่อมีข้อความใหม่เข้ามาคุณจะเลื่อนไปที่ด้านล่างของคอนเทนเนอร์โดยอัตโนมัติใน React ได้อย่างไร?
คำตอบ:
ตามที่ Tushar กล่าวไว้คุณสามารถเก็บ 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มาตรฐานที่นี่
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>
)
}
}
UPDATE:
ตอนนี้มี hooks แล้วฉันกำลังอัปเดตคำตอบเพื่อเพิ่มการใช้งานuseRef
และuseEffect
hooks ของจริงที่ใช้เวทมนตร์ (React refs และscrollIntoView
DOM method) ยังคงเหมือนเดิม:
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>
)
}
ทำโค้ดแซนด์บ็อกซ์ (พื้นฐานมาก) ด้วยหากคุณต้องการตรวจสอบพฤติกรรมhttps://codesandbox.io/s/scrolltobottomexample-f90lz
this.messagesEnd.current
มีอยู่เสมอ อย่างไรก็ตามสิ่งสำคัญคือต้องสังเกตว่าการโทรthis.messagesEnd.current
ก่อนการเรนเดอร์ครั้งแรกจะทำให้เกิดข้อผิดพลาดที่คุณชี้ ขอบคุณ
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
นั้นแย่มากในขณะนี้
ขอบคุณ @enl
เราควรหลีกเลี่ยงการใช้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();
}
}
}
react-scrollable-feedจะเลื่อนลงไปที่องค์ประกอบล่าสุดโดยอัตโนมัติหากผู้ใช้อยู่ที่ด้านล่างของส่วนที่เลื่อนได้แล้ว มิฉะนั้นจะปล่อยให้ผู้ใช้อยู่ในตำแหน่งเดิม ฉันคิดว่านี่มีประโยชน์มากสำหรับส่วนประกอบการแชท :)
ฉันคิดว่าคำตอบอื่น ๆ ที่นี่จะบังคับให้เลื่อนทุกครั้งไม่ว่าแถบเลื่อนจะอยู่ที่ใด ปัญหาอื่น ๆ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
ข้อจำกัดความรับผิดชอบ: ฉันเป็นเจ้าของแพ็คเกจ
อ้างอิงที่เก็บข้อความของคุณ
<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>
);
}
}
ฉันสร้างองค์ประกอบว่างในตอนท้ายของข้อความและเลื่อนไปที่องค์ประกอบนั้น ไม่จำเป็นต้องติดตามการอ้างอิง
หากต้องการทำด้วย React Hooks สามารถทำตามวิธีนี้ได้ สำหรับ div ดัมมี่ถูกวางไว้ที่ด้านล่างของแชท useRef Hook ถูกใช้ที่นี่
Hooks 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>
);
}
เวอร์ชัน 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
ฉันไม่สามารถรับคำตอบใด ๆ ด้านล่างสำหรับการทำงาน แต่ js ง่ายๆทำเคล็ดลับให้ฉัน:
window.scrollTo({
top: document.body.scrollHeight,
left: 0,
behavior: 'smooth'
});
ตัวอย่างการทำงาน:
คุณสามารถใช้scrollIntoView
วิธีDOM เพื่อทำให้ส่วนประกอบมองเห็นได้ในมุมมอง
สำหรับสิ่งนี้ในขณะที่แสดงผลคอมโพเนนต์ให้ระบุ ID อ้างอิงสำหรับองค์ประกอบ 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>
}
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการใช้โทษกับตอบสนองและ 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>'.
อื่น ๆ ...