programing

어떻게 반응해서 아래로 스크롤합니까?

lastmemo 2023. 2. 25. 19:42
반응형

어떻게 반응해서 아래로 스크롤합니까?

채팅 시스템을 구축하여 창으로 들어가거나 새로운 메시지가 들어오면 자동으로 아래로 스크롤하고 싶습니다.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 메서드를 사용하고 있습니다.

나는 단지 새로운 것에 맞춰 답을 업데이트하고 싶다.React.createRef() 방법은 같지만 기본적으로는 동일합니다.current다음 중 하나:

class Messages extends React.Component {

  const messagesEndRef = React.createRef()

  componentDidMount () {
    this.scrollToBottom()
  }
  componentDidUpdate () {
    this.scrollToBottom()
  }
  scrollToBottom = () => {
    this.messagesEndRef.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 및 "), "React refs" (React refs)scrollIntoViewDOM 식은 은은 dom dom dom dom 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>
  )
}

동작을 체크하려면 (매우 기본적인) 코드 앤 박스도 만들었습니다.https://codesandbox.io/s/scrolltobottomexample-f90lz

사용 안 함

참조가 있는 클래스 구성 요소

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 divRef = useRef(null);

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

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

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

참조:

제가 추천하고 싶은 가장 쉽고 좋은 방법은요.

My React JS 버전: 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"});
  }

위의 를 의의함 and의함 inside inside inside inside and and inside inside 。componentDidMount() ★★★★★★★★★★★★★★★★★」componentDidUpdate()


기능 컴포넌트(훅)의 경우

★★★useRef() ★★★★★★★★★★★★★★★★★」useEffect()

import { useEffect, useRef } from 'react'

함수 내(「」를 useState())

const messageRef = useRef();

페이지를 로드할 때 스크롤해야 한다고 가정해 봅시다.

useEffect(() => {
    if (messageRef.current) {
      messageRef.current.scrollIntoView(
        {
          behavior: 'smooth',
          block: 'end',
          inline: 'nearest'
        })
    }
  })

또는 액션이 실행된 후 트리거되도록 하려면

useEffect(() => {
  if (messageRef.current) {
    messageRef.current.scrollIntoView(
      {
        behavior: 'smooth',
        block: 'end',
        inline: 'nearest'
      })
  }
},
[stateVariable])

그리고 마지막으로 HTML 구조

return(
    <body>
        <div ref={messageRef}> // <= The only different is we are calling a variable here
            <div>Message 1</div>
            <div>Message 2</div>
            <div>Message 3</div>
        </div>
    </body>
)

은, 을 참조해 주세요.Element.scrollIntoView()developer.mozilla.org를 방문하다

콜백 레퍼런스의 자세한 내용은 reactjs.org를 참조하십시오.

사용자가 이미 스크롤 가능한 섹션의 맨 아래에 있는 경우 react-controllable-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

면책사항:나는 그 소포의 주인이다.

작업에 대한 아래 답변은 얻을 수 없었지만 단순 js는 나를 위해 성공했습니다.

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

리액트 훅을 사용하여 이 작업을 수행할 경우 이 방법을 따를 수 있습니다.더미 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>
  );
}

상위 답변의 crollIntoView(...) 접근 방식에는 두 가지 주요 문제가 있습니다.

  1. 부모 요소가 창 경계 밖으로 스크롤되면 페이지 전체가 스크롤되므로 의미론적으로 올바르지 않습니다.브라우저는 문자 그대로 요소를 표시하기 위해 필요한 모든 것을 스크롤합니다.

  2. useEffect()를 사용하는 기능 컴포넌트에서는 적어도 Chrome 96.0.4665.45에서는 신뢰할 수 없는 결과를 얻을 수 있습니다.useEffect()는 페이지 새로고침 시 너무 빨리 호출되어 스크롤이 실행되지 않습니다.setTimeout(... 0)을 사용하여 스크롤IntoView를 지연시키면 페이지 새로고침이 수정되지만 적어도 나에게는 새로고침 탭에 처음 로드되지 않습니다.

다음은 제가 사용하고 있는 솔루션입니다.솔루션의 견고성과 오래된 브라우저와의 호환성이 향상되었습니다.

function Chat() {
   const chatParent = useRef<HTMLDivElement(null);

   useEffect(() => {
      const domNode = chatParent.current;
      if (domNode) {
         domNode.scrollTop = domNode.scrollHeight;
      }
   })
   return (
      <div ref={chatParent}>
         ...
      </div>
   )
}

하시면 됩니다.ref컴포넌트를 추적합니다.

「 」를하는 있는 .ref컴포넌트( 컴포넌트투고해 !1개의 개별 컴포넌트(마지막 컴포넌트)를 투고해 주세요!

나에게 효과가 있다는 것을 알게 된 것은 다음과 같습니다.

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();
    }
  }
}
  1. 메시지 컨테이너를 참조합니다.

    <div ref={(el) => { this.messagesContainer = el; }}> YOUR MESSAGES </div>
    
  2. 메시지 컨테이너를 .scrollTop equal " " " "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>
        );
    }
}

메시지 끝에 빈 요소를 만들고 해당 요소로 스크롤했습니다.레퍼런스를 추적할 필요가 없습니다.

작업 예:

DOM 방법을 사용하여 구성요소를 보기에 표시할 수 있습니다.

위해 때를 DOM ID를 하여 지정하기만 하면 .ref기여하다.그런 다음 라이프 사이클에 따라 방법을 사용합니다.이 솔루션의 샘플 코드를 입력했을 뿐입니다.다음은 메시지가 수신될 때마다 표시되는 구성 요소 렌더링입니다.이 구성 요소를 렌더링하기 위한 코드/메서드를 작성해야 합니다.

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전달된 특정 채팅메시지의 고유 ID 입니다.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>
        );
    }
}

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

또 다른 옵션으로 리액트 스크롤 컴포넌트를 검토할 필요가 있습니다.

나는 그것을 다음과 같이 하는 것을 좋아한다.

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

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

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

TypeScript에서 이 문제를 해결하는 방법은 다음과 같습니다(ref를 사용하여 스크롤하는 대상 요소에 대한 참조 사용).

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와 함께 사용하는 방법에 대한 자세한 내용은 여기를 참조하십시오.

난 이거면 돼

messagesEndRef.current.scrollTop = messagesEndRef.current.scrollHeight

여기서 const messagesEndRef = useRef(); 사용

사용.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>
                    )
        }
}

이는 위의 답변에서 데이터 배열이 아닌 '자녀'를 지원하도록 수정되었습니다.

주의: 스타일 컴포넌트의 사용은 솔루션에서 중요하지 않습니다.

import {useEffect, useRef} from "react";
import React from "react";
import styled from "styled-components";

export interface Props {
    children: Array<any> | any,
}

export function AutoScrollList(props: Props) {
    const bottomRef: any = useRef();

    const scrollToBottom = () => {
        bottomRef.current.scrollIntoView({
            behavior: "smooth",
            block: "start",
        });
    };

    useEffect(() => {
        scrollToBottom()
    }, [props.children])

    return (
        <Container {...props}>
            <div key={'child'}>{props.children}</div>
            <div key={'dummy'} ref={bottomRef}/>
        </Container>
    );
}

const Container = styled.div``;

페이지 하단으로 스크롤하려면 먼저 페이지 하부에 있는 ID를 선택해야 합니다.그런 다음 document.getElementById를 사용하여 ID를 선택하고 ScrollIntoView()를 사용하여 아래로 스크롤할 수 있습니다.아래 코드를 참조해 주세요.

   scrollToBottom= async ()=>{
      document.getElementById('bottomID').scrollIntoView();
    }

mweb/web에서 이 문제에 직면해 있습니다.이 페이지에서는 모든 솔루션이 양호하지만 안드로이드 크롬 브라우저를 사용하는 동안 모든 솔루션이 작동하지 않습니다.그래서 mweb과 web은 몇 가지 사소한 수정으로 해결했습니다.

    import { createRef, useEffect } from 'react';
    import { useSelector } from 'react-redux';
    import { AppState } from 'redux/store';
    import Message from '../Message/Message';
    import styles from './MessageList.module.scss';
    
    const MessageList = () => {
      const messagesEndRef: any = createRef();
      const { messages } = useSelector((state: AppState) => state?.video);
      const scrollToBottom = () => {
          //this is not working in mWeb
            // messagesEndRef.current.scrollIntoView({
            //   behavior: 'smooth',
            //   block: 'end',
            //   inline: 'nearest',
            // });
         const scroll =
          messagesEndRef.current.scrollHeight -
           messagesEndRef.current.clientHeight;
         messagesEndRef.current.scrollTo(0, scroll);
      };
    
      useEffect(() => {
        if (messages.length > 3) {
          scrollToBottom();
        }
      }, [messages]);
    
      return (
        <section className={styles.footerTopSection} ref={messagesEndRef} >
          {messages?.map((message: any) => (
            <Message  key={message.id} {...message} />
          ))}
        </section>
      );
    };
    
    export default MessageList;

이것은 Kent C가 가르쳐 준 useLayoutEffect의 훌륭한 사용 사례입니다.도드.

https://kentcdodds.com/blog/useeffect-vs-uselayouteffect

효과가 (DOM 노드 참조를 통해) DOM을 변환하고 DOM 변환에 의해 렌더링된 시간과 변환된 효과 사이에 DOM 노드의 모양이 변경되는 경우, useEffect를 사용하지 않을 수 있습니다.

제 경우 div 하단에 동적으로 요소를 생성하고 있었기 때문에 작은 타임아웃을 추가해야 했습니다.

여기에 이미지 설명 입력

   const bottomRef = useRef<null | HTMLDivElement>(null);

    useLayoutEffect(() => {
        setTimeout(function () {
            if (bottomRef.current) bottomRef.current.scrollTop = bottomRef.current.scrollHeight;
        }, 10);
    }, [transactionsAmount]);
 const scrollingBottom = () => {
    const e = ref;

    e.current?.scrollIntoView({
      behavior: "smooth",
      block: "center",
      inline: "start",
    });
  };

 useEffect(() => {
    scrollingBottom();
  });

<span ref={ref}>{item.body.content}</span>
    

풀 버전(타입 스크립트):

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

언급URL : https://stackoverflow.com/questions/37620694/how-to-scroll-to-bottom-in-react

반응형