Tepki olarak nasıl aşağı kaydırılır?


127

Bir sohbet sistemi kurmak ve pencereye girerken ve yeni mesajlar geldiğinde otomatik olarak en alta kaydırmak istiyorum. React'te bir kabın altına otomatik olarak nasıl kayarsınız?

Yanıtlar:


222

Tushar'ın da belirttiği gibi, sohbetinizin altında bir kukla div bulundurabilirsiniz:

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

ve ardından bileşeniniz her güncellendiğinde ona gidin (yani, yeni mesajlar eklendikçe durum güncellenir):

scrollToBottom = () => {
  this.messagesEnd.scrollIntoView({ behavior: "smooth" });
}

componentDidMount() {
  this.scrollToBottom();
}

componentDidUpdate() {
  this.scrollToBottom();
}

Burada standart Element.scrollIntoView yöntemini kullanıyorum .


3
dokümantasyondan uyarı: "findDOMNode, işlevsel bileşenlerde kullanılamaz."
Tomasz Mularczyk

1
this.messagesEnd.scrollIntoView()benim için iyi çalıştı. Kullanmaya gerek yoktu findDOMNode().
Rajat Saxena

işlevi daha scrollToBottom(){this.scrollBottom.scrollIntoView({ behavior: 'smooth' })}yeni sürümlerde çalışması için değiştirildi
Kunok

2
Tamam, findDOMNode'u kaldırdım. Bu birisi için işe yaramazsa, cevabın düzenleme geçmişini kontrol edebilirsiniz.
metakermit

7
ScrollIntoView'un TypeError olduğu bir hatam var: Tanımlanmamış 'scrollIntoView' özelliği okunamıyor. Ne yapalım?
Feruza

90

Yanıtı yeni React.createRef() yöntemle eşleşecek şekilde güncellemek istiyorum , ancak temelde aynı, sadece currentoluşturulan ref içindeki özelliği aklınızda bulundurun :

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

GÜNCELLEME:

Artık kancalar mevcut, yanıtı useRefve useEffectkancaların kullanımını eklemek için güncelliyorum , sihri yapan gerçek şey (React refs ve scrollIntoViewDOM yöntemi) aynı kalıyor:

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

Ayrıca davranışı kontrol etmek istiyorsanız (çok basit) bir kod ve kutu oluşturdu https://codesandbox.io/s/scrolltobottomexample-f90lz


2
componentDidUpdate, React yaşam döngüsünde birçok kez çağırabilir. Bu nedenle, ref this.messagesEnd.current'ın scrollToBottom fonksiyonunda var olup olmadığını kontrol etmeliyiz. This.messagesEnd.current yoksa, hata mesajı TypeError gösterecektir: null 'scrollIntoView' özelliği okunamıyor. Öyleyse, şunu da ekleyin if koşulu scrollToBottom = () => {if (this.messagesEnd.current) {this.messagesEnd.current.scrollIntoView ({behaviour: 'smooth'})}}
Arpit

componentDidUpdate her zaman ilk oluşturmadan sonra gerçekleşir ( reactjs.org/docs/react-component.html#the-component-lifecycle ). Bu örnekte hiçbir hata olmamalı ve this.messagesEnd.currenther zaman var olmalıdır. Yine de this.messagesEnd.current, ilk işlemeden önce çağırmanın işaret ettiğiniz hatayla sonuçlanacağına dikkat etmek önemlidir . Thnx.
Diego Lara

this.messagesEndscrollTo yöntemindeki ilk örneğinizde ne var ?
dcsan

@dcsan bu bir React referansıdır, bunlar yeniden oluşturulduktan sonra bile bir DOM öğesini takip etmek için kullanılır. reactjs.org/docs/refs-and-the-dom.html#creating-refs
Diego Lara

1
İkinci örnek kod çalışmıyor. useEffectYöntem, ihtiyacı olan yerleştirilmesini () => {scrollToBottom()}. Yine de çok teşekkürler
Gaspar

36

Kullanmayın findDOMNode

Ref ile sınıf bileşenleri

class MyComponent extends Component {
  componentDidMount() {
    this.scrollToBottom();
  }

  componentDidUpdate() {
    this.scrollToBottom();
  }

  scrollToBottom() {
    this.el.scrollIntoView({ behavior: 'smooth' });
  }

  render() {
    return <div ref={el => { this.el = el; }} />
  }
}

Kancalı fonksiyon bileşenleri:

import React, { useRef, useEffect } from 'react';

const MyComponent = () => {
  const divRref = useRef(null);

  useEffect(() => {
    divRef.current.scrollIntoView({ behavior: 'smooth' });
  });

  return <div ref={divRef} />;
}

2
FindDOMNode'u neden kullanmamanız gerektiğini açıklayabilir misiniz?
one stevy boi

2
@steviekins "React'teki iyileştirmeleri engellediğinden" ve büyük olasılıkla kullanımdan kaldırılacağından github.com/yannickcr/eslint-plugin-react/issues/…
tgdn

2
Amerikan dilinde yazılmalıdır behavior(düzenleme yapılamaz çünkü "düzenlemeler en az 6 karakter olmalıdır",
Joe Freeman

1
için destek scrollIntoViewile smoothşu anda çok kötü.
Andreykul

@Andreykul, 'pürüzsüz' kullanarak benzer sonuçlar görüyor gibiyim. Tutarlı değil.
flimflam57

18

@Enlitement'e teşekkürler

kullanmaktan kaçınmalıyız , bileşenleri takip etmek için findDOMNodekullanabilirizrefs

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

referans:


Bu çözümü en uygun buluyorum çünkü DOM'a yeni (kukla) öğeler eklemiyor, ancak tam anlamıyla mevcut olanla ilgileniyor, teşekkürler jk2k
devplayer

7

Kullanabilirsiniz refBileşenleri takip etmek için s .

refTek bir bileşeni (sonuncusu) ayarlamanın bir yolunu biliyorsanız , lütfen gönderin!

İşte benim için çalıştığını bulduğum şey:

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

tepki-kaydırılabilir besleme , kullanıcı zaten kaydırılabilir bölümün altındaysa otomatik olarak en son öğeye kaydırır. Aksi takdirde kullanıcıyı aynı pozisyonda bırakacaktır. Bunun sohbet bileşenleri için oldukça faydalı olduğunu düşünüyorum :)

Sanırım buradaki diğer cevaplar, kaydırma çubuğu nerede olursa olsun her seferinde kaydırmaya zorlayacak. Diğer sorunscrollIntoView , kaydırılabilir div'iniz görünümde değilse tüm sayfayı kaydırmasıdır.

Şu şekilde kullanılabilir:

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

Yalnızca belirli bir sarmalayıcı bileşenine sahip olduğunuzdan emin olun. height veyamax-height

Sorumluluk reddi: Paketin sahibiyim


Teşekkürler, kontrolünü kullandım. Not: forceScroll = true kullanmam gerekiyordu, bu yüzden istediğim gibi çalışmasını sağladım, bazı nedenlerden dolayı kaydırma çubuğu görünmeye başladığında otomatik olarak en üste kaydırılmadı.
Patric

6
  1. Mesaj kapsayıcınıza başvurun.

    <div ref={(el) => { this.messagesContainer = el; }}> YOUR MESSAGES </div>
  2. Mesaj kapsayıcınızı bulun ve scrollTopözniteliğini eşitleyin scrollHeight:

    scrollToBottom = () => {
        const messagesContainer = ReactDOM.findDOMNode(this.messagesContainer);
        messagesContainer.scrollTop = messagesContainer.scrollHeight;
    };
  3. İlgili yöntem yukarıda uyandırmak componentDidMountve componentDidUpdate.

    componentDidMount() {
         this.scrollToBottom();
    }
    
    componentDidUpdate() {
         this.scrollToBottom();
    }

Bunu kodumda böyle kullanıyorum:

 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

Mesajların sonunda boş bir öğe oluşturdum ve o öğeye kaydırdım. Referansları takip etmeye gerek yok.


nasıl yaptın
Pedro JR

@mmla Safari'de karşılaştığınız sorun neydi? Güvenilir bir şekilde kaydırılmıyor mu?
Tushar Agarwal

5

Bunu React Hooks ile yapmak istiyorsanız, bu yöntem takip edilebilir. Sahte bir div, sohbetin altına yerleştirildi. useRef Hook burada kullanılır.

Hooks API Referansı: 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

Tavsiye edebileceğim en kolay ve en iyi yol.

ReactJS sürümüm: 16.12.0


render()İşlev içindeki HTML yapısı

    render()
        return(
            <body>
                <div ref="messageList">
                    <div>Message 1</div>
                    <div>Message 2</div>
                    <div>Message 3</div>
                </div>
            </body>
        )
    )

scrollToBottom()öğenin referansını alacak fonksiyon. ve scrollIntoView()işleve göre kaydırın .

  scrollToBottom = () => {
    const { messageList } = this.refs;
    messageList.scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"});
  }

ve yukarıdaki işlevi içeride çağırın componentDidMount()vecomponentDidUpdate()

hakkında daha fazla açıklama için Element.scrollIntoView()ziyaret developer.mozilla.org


Ref aslında kapsayıcıda değil ileti div'lerinde bildirilmelidir
toing_toing

4

Çalışmak için aşağıdaki cevaplardan hiçbirini alamadım ama basit js benim için hile yaptı:

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

3

Çalışma Örneği:

scrollIntoViewGörünümde bir bileşeni görünür kılmak için DOM yöntemini kullanabilirsiniz .

Bunun için, bileşeni oluştururken, yalnızca refözelliği kullanarak DOM öğesi için bir referans kimliği verin . Daha sonra yöntemi yaşam döngüsü scrollIntoViewüzerinde kullanın componentDidMount. Ben sadece bu çözüm için çalışan bir örnek kod koyuyorum. Aşağıda, her mesaj alındığında oluşturan bir bileşendir. Bu bileşeni oluşturmak için kod / yöntemler yazmalısınız.

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

İşte this.props.message.MessageIdolarak iletilen belirli sohbet mesajının benzersiz kimliğiprops


Şaşırtıcı Sherin bhai, pasta gibi çalışıyor.Teşekkürler
Mohammed Sarfaraz

@MohammedSarfaraz Yardımcı olabildiğime sevindim :)
Sherin Jose

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

Bunu şu şekilde yapmayı seviyorum.

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

İyi cevabı için teşekkür ederim 'metakermit', ama sanırım biraz daha iyi hale getirebiliriz, aşağı kaydırmak için şunu kullanmalıyız:

scrollToBottom = () => {
   this.messagesEnd.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
}

ancak yukarı kaydırmak istiyorsanız, şunu kullanmalısınız:

scrollToTop = () => {
   this.messagesEnd.scrollIntoView({ behavior: "smooth", block: "start", inline: "nearest" });
}   

ve bu kodlar yaygındır:

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

kullanma 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

Bunu TypeScript'te şu şekilde çözersiniz (kaydırdığınız hedeflenen öğeye ref kullanarak):

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

React ve Typescript ile ref kullanımı hakkında daha fazla bilgi için burada harika bir makale bulabilirsiniz .


-1

Tam sürüm (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>
    )
  }
}


bu benim için her türlü hatayı veriyor: Property 'scrollIntoView' does not exist on type 'RefObject<unknown>'. ve Type 'HTMLDivElement | null' is not assignable to type 'RefObject<unknown>'. Type 'null' is not assignable to type 'RefObject<unknown>'. bu yüzden ...
dcsan

ReactJS sürümü pls?
1.16.0
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.