262

I want to build a chat system and automatically scroll to the bottom when entering the window and when new messages come in. How do you automatically scroll to the bottom of a container in React?

helsont
  • 4,347
  • 4
  • 23
  • 28

26 Answers26

345

As Tushar mentioned, you can keep a dummy div at the bottom of your chat:

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

and then scroll to it whenever your component is updated (i.e. state updated as new messages are added):

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

componentDidMount() {
  this.scrollToBottom();
}

componentDidUpdate() {
  this.scrollToBottom();
}

I'm using the standard Element.scrollIntoView method here.

metakermit
  • 21,267
  • 15
  • 86
  • 95
  • 3
    warning from documentation: "findDOMNode cannot be used on functional components." – Tomasz Mularczyk Feb 22 '17 at 09:09
  • 3
    `this.messagesEnd.scrollIntoView()` worked fine for me. There was no need to use `findDOMNode()`. – Rajat Saxena Jun 07 '17 at 13:27
  • changed function to `scrollToBottom(){this.scrollBottom.scrollIntoView({ behavior: 'smooth' })}` to make it work in newer versions – Kunok Jun 26 '17 at 13:34
  • 2
    That will scroll to bottom even if you scroll up and it will mess up your UI experience. You need a flag to ignore scrolling to bottom on certain scenarios – Fanis Despoudis Jul 28 '17 at 14:29
  • since this is the top answer, I am wondering if it could be updated to no longer use `findDOMNode`. there seem to be multiple warnings here against its usage, mainly by @tgdn below who mentions that it is expected to become deprecated – one stevy boi Dec 06 '17 at 14:54
  • 2
    Ok, I removed findDOMNode. If this doesn't work for someone, you can check the answer's edit history. – metakermit Dec 08 '17 at 15:21
  • It stores a reference to the last div's DOM element in an instance field (`this.messagesEnd`). For more info on using of refs in React see [here](https://reactjs.org/docs/refs-and-the-dom.html). – metakermit Jan 24 '18 at 15:25
  • 10
    I have an error that scrollIntoView is TypeError: Cannot read property 'scrollIntoView' of undefined. What to do? – Feruza Feb 06 '18 at 11:55
  • Use `ref={el => (this.messagesEnd = el)}` instead and place `this.scrollBottom` as first line of code into `componentDidUpdate()` – Matthis Kohli Apr 07 '19 at 23:04
  • It works for me, but only if I use setTimeout in the scrollToBottom function. Anyone knows what I'm doing wrong? – Kurt Lagerbier Apr 25 '19 at 15:44
  • I am getting also this error `scrollIntoView` is `undefined` – K.S Sep 26 '19 at 07:55
  • I am getting: Warning: validateDOMNesting(...):
    cannot appear as a child of .
    – Fernando Santiago Nov 28 '19 at 22:37
  • 2
    Worked fine until i was using it to send message and scroll to latest message in chat window. but on loading messages from server when user first initiate, kind of moves to half the length of scrollbar, the target div is till way out of view. Any Ideas what could be wrong ? – Mayank Tiwari May 21 '20 at 22:31
  • Note: sometimes it causes the whole page to move, in that case this code should fix it: `scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' })` – Dorki May 26 '20 at 21:03
  • 2
    Nobody mentioned Safari & IE does not support "smooth" behavior. – JKhan Jun 30 '20 at 18:59
  • 1
    Had to make 2 adjustments to pass tests. 1. `if (this.messagesEnd) { this.messagesEnd.scrollIntoView()}` or `this.messagesEnd?.scrollIntoView()` to prevent undefined error. 2. Insert `window.HTMLElement.prototype.scrollIntoView = function () {};` into test functions to prevent "scrollIntoView isn't a function" errors. See [Issue](https://github.com/jsdom/jsdom/issues/1695) – Bix Jul 09 '20 at 22:24
  • can anyone answer Fezura's question because I'm having the same thing – Raphael Morgan Dec 01 '21 at 08:03
291

I just want to update the answer to match the new React.createRef() method, but it's basically the same, just have in mind the current property in the created ref:

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

UPDATE:

Now that hooks are available, I'm updating the answer to add the use of the useRef and useEffect hooks, the real thing doing the magic (React refs and scrollIntoView DOM method) remains the same:

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

Also made a (very basic) codesandbox if you wanna check the behaviour https://codesandbox.io/s/scrolltobottomexample-f90lz

Diego Lara
  • 3,059
  • 1
  • 10
  • 13
  • 7
    componentDidUpdate can call many times in React lifecycle. So, we should check ref this.messagesEnd.current is exist or not in scrollToBottom function. If this.messagesEnd.current does not exist then error message will show TypeError: Cannot read property 'scrollIntoView' of null. So, add this if condition also scrollToBottom = () => { if (this.messagesEnd.current) { this.messagesEnd.current.scrollIntoView({ behavior: 'smooth' }) } } – Arpit Dec 01 '18 at 20:00
  • componentDidUpdate always happens after the first render (https://reactjs.org/docs/react-component.html#the-component-lifecycle). In this example, there should be no errors and `this.messagesEnd.current` always exists. Nevertheless it's important to notice that calling `this.messagesEnd.current` before the first render will result in the error you pointed. Thnx. – Diego Lara Feb 05 '19 at 17:24
  • what is `this.messagesEnd` in your first example in the scrollTo method? – dcsan Nov 17 '19 at 23:48
  • @dcsan it's a React ref, those are used to keep track of a DOM element even after rerenders. https://reactjs.org/docs/refs-and-the-dom.html#creating-refs – Diego Lara Nov 21 '19 at 17:06
  • 1
    The second example code doesn't works. The `useEffect` method need to be placed with `() => {scrollToBottom()}`. Thanks very much anyway – Gaspar Jan 24 '20 at 00:39
  • Thanks! When using this on a div with an overflow: scroll it also seems to scroll the whole window. – Ben Southall Mar 01 '20 at 07:17
  • Seems to me there is no acceptable solutions for nice scroll whithout help of some module like "react-scroll" etc .. – Goran_Ilic_Ilke Mar 14 '20 at 07:12
  • 3
    I had an error with this implementation of `useRef`: "current" is `null` until render. To fix, I put `if (messagesEndRef.current)` in the `scrollToBottom function. – Jericho Jan 20 '21 at 14:40
  • 10
    to fix the typescript error 'TypeScript error: Property 'scrollIntoView' does not exist on type 'never'. TS2339' -> assign the correct type with useRef: const scrollRef = useRef(null) – Hannes Schaletzky Feb 27 '21 at 21:28
  • For me, useEffect did not update on [messages], because the array itself has not changed. I used [messages.length] instead. – Little Brain Jun 21 '21 at 17:27
  • If we have parent div of messages div doesnt work,broke the element. – Goran_Ilic_Ilke Sep 20 '21 at 15:28
  • 1
    @DiegoLara the first example (Class component) has a typo, you typed this.messagesEnd.current which is wrong, it should be this.messagesEndRef.current. – joedotnot Oct 10 '21 at 03:09
  • For TypeScript see this answer: https://stackoverflow.com/questions/57028500/typescript-error-property-scrollintoview-does-not-exist-on-type-never-ts23 – Jaich Feb 25 '22 at 19:18
  • 1
    If you're using TypeScript, you need to do `useRef(null)` instead. – Marco Castanho Jul 12 '22 at 09:45
  • I'm having an issue with this solution because I have a Spinner spinning while the messages are being loaded, which means that the `
    ` is only rendered after I have the messages, which means that `messagesEndRef.current` is always `null`. I've tried removing the spinner and it works fine, but I want to keep it. Anyone has any idea how I fix this?
    – Marco Castanho Jul 12 '22 at 10:26
  • I have figured out how: moving the messages list to a separate component, that only gets rendered when the Spinner stops spinning, and having the scroll bottom mechanism inside that MessagesList component. – Marco Castanho Jul 12 '22 at 10:45
  • This is not a solution because the whole page gets scrolled too instead of just that 1 region within the div – busuu Jan 21 '23 at 18:40
  • 1
    @busuu I fixed that by changing the scrollToBottom function, and getting rid of the dummy
    and instead putting the ref on the main container div. Then if you use .scrollTo and go to the scrollHeight of the current ref instead of .scrollIntoView, it works: const scrollToBottom = () => { messagesEndRef.current?.scrollTo(0, messagesEndRef.current?.scrollHeight); };
    – DiscDev Mar 05 '23 at 22:19
76

Do not use findDOMNode because "because it blocks certain improvements in React in the future"

Class components with ref

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

  componentDidUpdate() {
    this.scrollToBottom();
  }

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

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

Function components with hooks:

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

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

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

  return <div ref={divRef} />;
}
Thomas Gak-Deluen
  • 2,759
  • 2
  • 28
  • 38
35

Thanks to @enlitement

we should avoid using findDOMNode, we can use refs to keep track of the components

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

reference:

jk2K
  • 4,279
  • 3
  • 37
  • 40
27

The easiest and best way I would recommend is.

My ReactJS version: 16.12.0


For Class Components

HTML structure inside render() function

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

scrollToBottom() function which will get reference of the element. and scroll according to scrollIntoView() function.

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

and call the above function inside componentDidMount() and componentDidUpdate()


For Functional Components (Hooks)

Import useRef() and useEffect()

import { useEffect, useRef } from 'react'

Inside your export function, (same as calling a useState())

const messageRef = useRef();

And let's assume you have to scroll when page load,

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

OR if you want it to trigger once an action performed,

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

And Finally, to your HTML structure

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

for more explanation about Element.scrollIntoView() visit developer.mozilla.org

More detailed explanation in Callback refs visit reactjs.org

Ahamed Rasheed
  • 751
  • 9
  • 10
21

react-scrollable-feed automatically scrolls down to the latest element if the user was already at the bottom of the scrollable section. Otherwise, it will leave the user at the same position. I think this is pretty useful for chat components :)

I think the other answers here will force scroll everytime no matter where the scrollbar was. The other issue with scrollIntoView is that it will scroll the whole page if your scrollable div was not in view.

It can be used like this :

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

Just make sure to have a wrapper component with a specific height or max-height

Disclaimer: I am the owner of the package

Gabriel Bourgault
  • 842
  • 11
  • 22
  • Thanks, I used your control. Note: I had to use forceScroll=true so make it work as desired, for some reason it didn't scroll automatically to the top when the scrollbar began to appear. – Patric Aug 06 '20 at 12:30
  • @Patric if you could [open an issue on GitHub](https://github.com/dizco/react-scrollable-feed/issues/new?template=bug_report.md) with some details about your setup, maybe we can figure out what's wrong? – Gabriel Bourgault Aug 11 '20 at 21:49
  • 1
    This is exactly what I wanted, the control to scroll only when needed and `ScrollIntoView` was only scrolling a little bit, not to the bottom. – Lalit Fauzdar Aug 24 '21 at 14:48
14

I could not get any of below answers to work but simple js did the trick for me:

  window.scrollTo({
  top: document.body.scrollHeight,
  left: 0,
  behavior: 'smooth'
});
0xRLA
  • 3,279
  • 4
  • 30
  • 42
11

There are two major problems with the scrollIntoView(...) approach in the top answers:

  1. it's semantically incorrect, as it causes the entire page to scroll if your parent element is scrolled outside the window boundaries. The browser literally scrolls anything it needs to in getting the element visible.

  2. in a functional component using useEffect(), you get unreliable results, at least in Chrome 96.0.4665.45. useEffect() gets called too soon on page reload and the scroll doesn't happen. Delaying scrollIntoView with setTimeout(..., 0) fixes it for page reload, but not first load in a fresh tab, at least for me. shrugs

Here's the solution I've been using, it's solid and is more compatible with older browsers:

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

   useEffect(() => {
      const domNode = chatParent.current;
      if (domNode) {
         domNode.scrollTop = domNode.scrollHeight;
      }
   })
   return (
      <div ref={chatParent}>
         ...
      </div>
   )
}
Kai Arnold
  • 121
  • 1
  • 5
  • This works great! Additionally, I'd add some threshold when to do the auto-scrolling, you don't want to scroll if the user is looking at the history, right? – Crysfel Dec 01 '21 at 16:43
  • @Crysfel I agree, but I don't know if browsers have a scroll start/end event, in which case there's no clean way to pull this off without implementing your own scrollbars or hacking something. Possible hacks: use the scroll event and a timer, use mousedown/mouseup and some guesswork, or simply don't scroll when the mouse is down or chat window has focus. – Kai Arnold Dec 02 '21 at 18:39
  • Lil' typo in the generic usage of your `useRef`, @KaiArnold. Throw in another `>`! – jmealy Feb 05 '22 at 16:50
  • Works well. Is there a way the scroll event can be animated? – Prajwal Kulkarni May 07 '22 at 15:52
10

If you want to do this with React Hooks, this method can be followed. For a dummy div has been placed at the bottom of the chat. useRef Hook is used here.

Hooks API Reference : 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>
  );
}
Chathurika Senani
  • 716
  • 2
  • 9
  • 25
9

You can use refs to keep track of the components.

If you know of a way to set the ref of one individual component (the last one), please post!

Here's what I found worked for me:

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();
    }
  }
}
helsont
  • 4,347
  • 4
  • 23
  • 28
8

I created a empty element in the end of messages, and scrolled to that element. No need of keeping track of refs.

Tushar Agarwal
  • 366
  • 4
  • 9
7
  1. Reference your messages container.

    <div ref={(el) => { this.messagesContainer = el; }}> YOUR MESSAGES </div>
    
  2. Find your messages container and make its scrollTop attribute equal scrollHeight:

    scrollToBottom = () => {
        const messagesContainer = ReactDOM.findDOMNode(this.messagesContainer);
        messagesContainer.scrollTop = messagesContainer.scrollHeight;
    };
    
  3. Evoke above method on componentDidMount and componentDidUpdate.

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

This is how I am using this in my code:

 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>
        );
    }
}
Marcin Rapacz
  • 616
  • 8
  • 12
3

Working Example:

You can use the DOM scrollIntoView method to make a component visible in the view.

For this, while rendering the component just give a reference ID for the DOM element using ref attribute. Then use the method scrollIntoView on componentDidMount life cycle. I am just putting a working sample code for this solution. The following is a component rendering each time a message received. You should write code/methods for rendering this component.

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

Here this.props.message.MessageId is the unique ID of the particular chat message passed as props

Sherin Jose
  • 2,508
  • 3
  • 18
  • 39
3

This is a great usecase for useLayoutEffect as taught by Kent C. Dodds.

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

if your effect is mutating the DOM (via a DOM node ref) and the DOM mutation will change the appearance of the DOM node between the time that it is rendered and your effect mutates it, then you don't want to use useEffect.

In my case i was dynamically generating elements at the bottom of a div so i had to add a small timeout.

enter image description here

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

    useLayoutEffect(() => {
        setTimeout(function () {
            if (bottomRef.current) bottomRef.current.scrollTop = bottomRef.current.scrollHeight;
        }, 10);
    }, [transactionsAmount]);
Stanley
  • 2,434
  • 18
  • 28
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>
        );
    }
}
Pavan Garre
  • 2,661
  • 1
  • 18
  • 19
2

thank you 'metakermit' for his good answer, but I think we can make it a bit better, for scroll to bottom, we should use this:

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

but if you want to scroll top, you should use this:

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

and this codes are common:

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>
  );
}
Abolfazl Miadian
  • 3,099
  • 2
  • 19
  • 15
1

As another option it is worth looking at react scroll component.

John
  • 29,788
  • 18
  • 89
  • 130
1

I like doing it the following way.

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

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

render(){
  return(
    <div ref={`thing`}>
      <ManyThings things={}>
    </div>
  )
}
aestrro
  • 963
  • 10
  • 10
1

This is how you would solve this in TypeScript (using the ref to a targeted element where you scroll to):

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

For more information about using ref with React and Typescript you can find a great article here.

Daniel Budick
  • 1,780
  • 1
  • 18
  • 19
1

This works for me

messagesEndRef.current.scrollTop = messagesEndRef.current.scrollHeight

where const messagesEndRef = useRef(); to use

kta
  • 19,412
  • 7
  • 65
  • 47
0

Using 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>
                    )
        }
}
akash
  • 727
  • 5
  • 13
0

This is modified from an answer above to support 'children' instead of a data array.

Note: The use of styled-components is of no importance to the solution.

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``;
guyman
  • 158
  • 1
  • 10
0

In order to scroll down to the bottom of the page first we have to select an id which resides at the bottom of the page. Then we can use the document.getElementById to select the id and scroll down using scrollIntoView(). Please refer the below code.

   scrollToBottom= async ()=>{
      document.getElementById('bottomID').scrollIntoView();
    }
Senthuran
  • 1,583
  • 2
  • 15
  • 19
0

I have face this problem in mweb/web.All the solution is good in this page but all the solution is not working while using android chrome browser . So for mweb and web I got the solution with some minor fixes.

    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;
Narendra Maurya
  • 391
  • 2
  • 7
0
 const scrollingBottom = () => {
    const e = ref;

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

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

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

Full version (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>
    )
  }
}
TechTurtle
  • 2,667
  • 4
  • 22
  • 31
  • this gives all kind of errors for me: `Property 'scrollIntoView' does not exist on type 'RefObject'.` and `Type 'HTMLDivElement | null' is not assignable to type 'RefObject'. Type 'null' is not assignable to type 'RefObject'.` so... – dcsan Jul 02 '19 at 17:28
  • Version of ReactJS pls? I m using 1.16.0 – TechTurtle Jul 08 '19 at 17:41