反応で下にスクロールする方法?


126

チャットシステムを構築して、ウィンドウに入ったときや新しいメッセージが届いたときに自動的に一番下までスクロールしたいのですが、Reactでコンテナーの一番下まで自動的にスクロールするにはどうすればよいですか?

回答:


221

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メソッドを使用しています。


3
ドキュメントからの警告:「findDOMNodeは機能コンポーネントでは使用できません。」
Tomasz Mularczyk 2017

1
this.messagesEnd.scrollIntoView()私にとってはうまくいきました。使用する必要はありませんでしたfindDOMNode()
Rajat Saxena 2017年

scrollToBottom(){this.scrollBottom.scrollIntoView({ behavior: 'smooth' })}新しいバージョンで機能するように機能を変更
Kunok

2
わかりました、findDOMNodeを削除しました。これがうまくいかない場合は、回答の編集履歴を確認できます。
metakermit 2017

7
scrollIntoViewがTypeErrorであるというエラーがあります。未定義のプロパティ 'scrollIntoView'を読み取れません。何をすべきか?
Feruza

87

新しいReact.createRef() メソッドに一致するように回答を更新したいだけですが、基本的には同じcurrentです。作成されたrefのプロパティを念頭に置いてください。

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 and useEffectフックの魔法(React refsとscrollIntoViewDOMメソッド)を実行する本当のことは同じです。

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
componentDidUpdateは、Reactライフサイクルで何度も呼び出すことができます。したがって、ref this.messagesEnd.currentがscrollToBottom関数に存在するかどうかを確認する必要があります。this.messagesEnd.currentが存在しない場合、エラーメッセージにTypeError:nullのプロパティ 'scrollIntoView'を読み取れません。したがって、this if条件も追加してくださいscrollToBottom =()=> {if(this.messagesEnd.current){this.messagesEnd.current.scrollIntoView({behavior: 'smooth'})}}
Arpit

componentDidUpdateは、最初のレンダリングの後に常に発生します(reactjs.org/docs/react-component.html#the-component-lifecycle)。この例では、エラーはなく、this.messagesEnd.current常に存在しています。それでもthis.messagesEnd.current、最初のレンダリングの前に呼び出すと、指摘したエラーが発生することに注意することが重要です。Thnx。
ディエゴ・ララ

this.messagesEndscrollToメソッドの最初の例には何がありますか?
dcsan

@dcsanこれはReactの参照です。これらは、再レンダリング後もDOM要素を追跡するために使用されます。reactjs.org/docs/refs-and-the-dom.html#creating-refs
Diego Lara

1
2番目のサンプルコードは機能しません。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
findDOMNodeを使用すべきでない理由を説明できますか?
1つのstevy boi

2
@steviekins「Reactの特定の改善をブロックする」ため、github.com
yannickcr /

2
アメリカのスペルであるbehavior必要があります(「編集内容は少なくとも6文字である必要があるため、編集できません」)。
ジョーフリーマン

1
現在、scrollIntoViewwithのサポートsmoothは非常に貧弱です。
Andreykul

@Andreykul、私は 'smooth'を使用して同様の結果を見ているようです。一貫していません。
flimflam57 2018年

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

refsを使用して、コンポーネントを追跡できます。

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

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

反応スクロール可能なフィードは、ユーザーが既にスクロール可能なセクションの下部にいた場合、最新の要素まで自動的にスクロールします。それ以外の場合は、ユーザーが同じ位置に残ります。これはチャットコンポーネントに非常に役立つと思います。

ここでの他の答えは、スクロールバーがどこにあっても毎回強制的にスクロールすると思います。もう1つの問題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

免責事項:私はパッケージの所有者です



5

React Hooksでこれを行う場合は、この方法に従うことができます。ダミーのdivはチャットの下部に配置されています。ここではuseRefフックを使用しています。

フック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>
  );
}

5

私がお勧めする最も簡単で最良の方法は、

私のReactJSバージョン: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()

developer.mozilla.orgにElement.scrollIntoView()アクセスて詳細を確認してください


refは、コンテナではなくメッセージ
divで

4

以下の答えはどれも機能しませんでしたが、単純なjsでうまくいきました:

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

3

実例:

DOM scrollIntoViewメソッドを使用して、コンポーネントをビューに表示できます。

このため、コンポーネントのレンダリング中は、ref属性を使用してDOM要素の参照IDを指定するだけです。次にscrollIntoViewcomponentDidMountライフサイクルでメソッドを使用します。私はこのソリューションのための実用的なサンプルコードを置いています。以下は、メッセージが受信されるたびにレンダリングされるコンポーネントです。このコンポーネントをレンダリングするためのコード/メソッドを記述する必要があります。

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


アメージングシェリンバハイケーキのように機能します。ありがとう
Mohammed Sarfaraz

@MohammedSarfarazよろしくお願いします:)
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

次のようにするのが好きです。

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

これは、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>
}

ReactとTypescriptでrefを使用する方法の詳細については、こちらの優れた記事を参照してください


-1

フルバージョン(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>'. ...
dcsan

ReactJS plsのバージョン?私は1.16.0を使用しています
TechTurtle
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.