कैसे प्रतिक्रिया में नीचे स्क्रॉल करने के लिए?


127

मैं एक चैट सिस्टम बनाना चाहता हूं और विंडो में प्रवेश करते समय और नीचे नए संदेश आने पर स्वचालित रूप से नीचे स्क्रॉल करता हूं। आप कैसे रिएक्ट में कंटेनर के निचले भाग में स्वचालित रूप से स्क्रॉल करते हैं?

जवाबों:


222

जैसा कि तुषार ने उल्लेख किया है, आप अपनी चैट के निचले भाग में एक डमी 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 विधि का उपयोग कर रहा हूँ ।


3
प्रलेखन से चेतावनी: "findDOMNode का उपयोग कार्यात्मक घटकों पर नहीं किया जा सकता है।"
टॉमाज़ मूलारस्क

1
this.messagesEnd.scrollIntoView()मेरे लिए ठीक काम किया। उपयोग करने की कोई आवश्यकता नहीं थी findDOMNode()
रजत सक्सेना

scrollToBottom(){this.scrollBottom.scrollIntoView({ behavior: 'smooth' })}नए संस्करणों में काम करने के लिए परिवर्तित कार्य
कुनोक

2
ठीक है, मैंने findDOMNode को हटा दिया। यदि यह किसी के लिए काम नहीं करता है, तो आप उत्तर के इतिहास को संपादित कर सकते हैं।
मेटाकारिट

7
मेरे पास एक त्रुटि है कि scrollIntoView TypeError है: अपरिभाषित की संपत्ति 'scrollIntoView' नहीं पढ़ सकता है। क्या करें?
फेरूजा

90

मैं सिर्फ नई 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डोम विधि) वही रहती है:

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


2
ComponentsDidUpdate रिएक्ट जीवनचक्र में कई बार कॉल कर सकता है। इसलिए, हमें यह देखना चाहिए कि यह.m.magesages.nd.current स्क्रॉलटॉबॉटम फ़ंक्शन में मौजूद है या नहीं। यदि यह.messagesEnd.current मौजूद नहीं है, तो त्रुटि संदेश TypeError दिखाएगा: नल की संपत्ति 'ScrollIntoView' नहीं पढ़ सकता है। तो, अगर इस ऐड हालत भी scrollToBottom = () => {अगर (this.messagesEnd.current) {this.messagesEnd.current.scrollIntoView ({व्यवहार: 'चिकनी'})}}
अर्पित

ComponentsDidUpdate हमेशा पहले रेंडर ( reactjs.org/docs/react-component.html#the-component-lifecycle ) के बाद होता है । इस उदाहरण में, कोई त्रुटि नहीं होनी चाहिए और this.messagesEnd.currentहमेशा मौजूद रहना चाहिए । फिर भी यह ध्यान रखना महत्वपूर्ण है कि this.messagesEnd.currentपहले रेंडर से पहले कॉल करने से आपके द्वारा बताई गई त्रुटि हो जाएगी। Thnx।
डिएगो लारा

this.messagesEndस्क्रोल्टो विधि में आपके पहले उदाहरण में क्या है ?
dcsan

@dcsan यह एक रिएक्ट रेफ है, जिनका उपयोग रेंडर्स के बाद भी DOM तत्व का ट्रैक रखने के लिए किया जाता है। reactjs.org/docs/refs-and-the-dom.html#creating-refs
डिएगो लारा

1
दूसरा उदाहरण कोड काम नहीं करता है। useEffectविधि की जरूरत के साथ रखा गया () => {scrollToBottom()}। वैसे भी बहुत बहुत धन्यवाद
Gaspar

36

प्रयोग नहीं करें 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} />;
}

2
क्या आप बता सकते हैं कि आपको doDOMNode का उपयोग क्यों नहीं करना चाहिए?
एक स्टीवी बोई

2
@steviekins क्योंकि "यह रिएक्ट में कुछ सुधार को अवरुद्ध करता है" और संभवतः पदावनत github.com/yannickcr/eslint-plugin-react/issues/…
tgdn

2
अमेरिकी वर्तनी होनी चाहिए behavior(संपादित नहीं कर सकते क्योंकि "संपादन कम से कम 6 वर्ण होना चाहिए", आह)।
जो फ्रीमैन

1
के scrollIntoViewसाथ समर्थन smoothफिलहाल बहुत खराब है।
एंड्रीकुल

@Andreykul, मुझे लगता है कि 'चिकनी' का उपयोग करने के साथ इसी तरह के परिणाम दिखाई दे रहे हैं। यह सुसंगत नहीं है।
flimflam57

18

@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();
}

संदर्भ:


मुझे यह समाधान सबसे उपयुक्त लगता है, क्योंकि यह DOM में नए (डमी) तत्वों को नहीं जोड़ता है, लेकिन मौजूदा के साथ शाब्दिक रूप से व्यवहार करता है, धन्यवाद jk2k
devplayer

7

आप 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();
    }
  }
}

7

यदि उपयोगकर्ता पहले से ही स्क्रॉल करने योग्य अनुभाग के निचले भाग में था, तो प्रतिक्रिया-स्क्रॉल करने योग्य-फ़ीड स्वचालित रूप से नवीनतम तत्व तक पहुंच जाता है। अन्यथा, यह उपयोगकर्ता को उसी स्थिति में छोड़ देगा। मुझे लगता है कि यह चैट घटकों के लिए बहुत उपयोगी है :)

मुझे लगता है कि यहां अन्य उत्तर हर बार स्क्रॉल को बाध्य करेंगे, चाहे जहां भी स्क्रॉलबार हो। इसके साथ दूसरा मुद्दा 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

अस्वीकरण: मैं पैकेज का मालिक हूं


धन्यवाद, मैंने आपके नियंत्रण का उपयोग किया। नोट: मुझे ForceScroll = true का उपयोग करना था इसलिए इसे वांछित तरीके से काम करें, किसी कारण से यह शीर्ष पर स्वचालित रूप से स्क्रॉल नहीं हुआ जब स्क्रॉलबार दिखाई देना शुरू हुआ।
पैट्रिक

6
  1. अपने संदेश कंटेनर संदर्भ।

    <div ref={(el) => { this.messagesContainer = el; }}> YOUR MESSAGES </div>
  2. अपने संदेश कंटेनर ढूंढें और उसकी scrollTopविशेषता समान बनाएं scrollHeight:

    scrollToBottom = () => {
        const messagesContainer = ReactDOM.findDOMNode(this.messagesContainer);
        messagesContainer.scrollTop = messagesContainer.scrollHeight;
    };
    
  3. विधि 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>
        );
    }
}

6

मैंने संदेशों के अंत में एक खाली तत्व बनाया, और उस तत्व को स्क्रॉल किया। रेफरी पर नज़र रखने की कोई आवश्यकता नहीं है।


आपने इसे कैसे किया
पेड्रो JR

@mmla आपको सफारी से क्या समस्या थी? क्या यह मज़बूती से स्क्रॉल नहीं करता है?
तुषार अग्रवाल

5

यदि आप रिएक्ट हुक के साथ ऐसा करना चाहते हैं, तो इस विधि का पालन किया जा सकता है। एक डमी div के लिए चैट के नीचे रखा गया है। useRef Hook का उपयोग यहाँ किया जाता है।

हुक एपीआई संदर्भ: 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>
  );
}

5

सबसे आसान और सबसे अच्छा तरीका मैं सुझाऊंगा।

मेरा रिएक्टज संस्करण: 16.12.0


render()फ़ंक्शन के अंदर HTML संरचना

    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


THe ref वास्तव में संदेश divs में घोषित किया जाना चाहिए, कंटेनर में नहीं
toing_toing

4

मुझे काम करने के लिए नीचे दिए गए जवाबों में से कोई भी नहीं मिला, लेकिन सरल js ने मेरे लिए चाल चली:

  window.scrollTo({
  top: document.body.scrollHeight,
  left: 0,
  behavior: 'smooth'
});

3

काम करने का उदाहरण:

आप scrollIntoViewदृश्य में एक घटक को दृश्यमान बनाने के लिए DOM विधि का उपयोग कर सकते हैं ।

इसके लिए, घटक का प्रतिपादन करते समय, refविशेषता का उपयोग करते हुए DOM तत्व के लिए एक संदर्भ ID दें । फिर जीवन चक्र 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


कमाल शेरिन भाई यह एक केक की तरह काम कर रहा है। धन्यवाद
मोहम्मद सरफराज

@MohammedSarfaraz ग्लेड I मदद कर सकता है :)
शेरिन जोस

2
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>
        );
    }
}

1

मैं इसे निम्नलिखित तरीके से करना पसंद करता हूं।

componentDidUpdate(prevProps, prevState){
  this.scrollToBottom();
}

scrollToBottom() {
  const {thing} = this.refs;
  thing.scrollTop = thing.scrollHeight - thing.clientHeight;
}

render(){
  return(
    <div ref={`thing`}>
      <ManyThings things={}>
    </div>
  )
}

1

उसके अच्छे उत्तर के लिए आपको 'मेटाकर्मीट' धन्यवाद, लेकिन मुझे लगता है कि हम इसे थोड़ा बेहतर बना सकते हैं, नीचे स्क्रॉल करने के लिए, हमें इसका उपयोग करना चाहिए:

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>
  );
}


0

का उपयोग करते हुए 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>
                    )
        }
}

0

यह है कि आप इसे टाइपस्क्रिप्ट में कैसे हल करेंगे (लक्षित तत्व के रेफरी का उपयोग करके जहां आप स्क्रॉल करते हैं):

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>
}

प्रतिक्रिया और टाइपस्क्रिप्ट के साथ रेफ का उपयोग करने के बारे में अधिक जानकारी के लिए आप यहां एक शानदार लेख पा सकते हैं ।


-1

पूर्ण संस्करण (टाइपस्क्रिप्ट):

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>'. इसी तरह ...
dcsan

ReactJS pls का संस्करण? मैं 1.16.0 का उपयोग कर रहा हूँ
TechTurtle
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.