user254153
user254153

Reputation: 1883

React PerfectScrollBar scroll at bottom of container for dynamically generated content

I am using react perfectscrollbar https://www.npmjs.com/package/react-perfect-scrollbar

I am trying to implement the chat feature where on clicked on user, the chat message is requested via ajax and placed in the DOM. Here, I want the container to be scrolled at bottom.

I have implemented below code in react but it seems to be not working. Ant idea? Note: I have filtered the code and only pasted the related section. I have used the suggested method for scrollTop but it's not working.

 class Complain extends Component {
    constructor(props) {
        super(props);
        
        this.chatContainerRef = React.createRef();

    }
    
        viewComplain(e, id, name) {
        axios.get('/complain/' + id, {

        })
            .then(res => {
                const chatmsg = res.data.data;
                // let data = { msgdata: chatmsg, name: name, complainid: id };
                this.setState({ chatmsg: chatmsg });
                this.setState({ complainname: name });
                this.setState({ activeChat: id });
                this.setState({ complainRegNo: chatmsg[0].regno });
                console.log(this.chatContainerRef);
                this.chatContainerRef.current.scrollTop = 200;  // -> this is not working
            })
            .catch(function (error) {

            });
    }

render(
return {
    
 <PerfectScrollbar option={{ suppressScrollX: false }} ref={this.chatContainerRef}>
       {
              this.state.chatmsg.map((post, i) => (
                  <div key={i} className={(post.msg_creater === "school") ? "row flex-nowrap message-row contact p-4" : "row flex-nowrap message-row user p-4"}>
                  <img className="avatar mr-4" src="../assets/images/avatars/profile.jpg" alt="" />
                  <div className="bubble">
                      <div className="message">{post.msg}</div>
                      <div className="time text-muted text-right mt-2">{post.created_at}</div>
                  </div>
              </div>
           ))
       }
      </PerfectScrollbar>
})

Upvotes: 2

Views: 7549

Answers (4)

Yanislav Tankov
Yanislav Tankov

Reputation: 61

Another option is to wrap the in custom component and set a prop for it's behaviour (up or down depending of scrolDown prop) and set to bottom depending of dynamically added content (children). For messaging app for example.

import { PositionProps } from '...styled';
import PerfectScrollbar from 'react-perfect-scrollbar';
import 'react-perfect-scrollbar/dist/css/styles.css';
import React, { useEffect, useRef } from 'react';

type TProps = PositionProps & {
  children: React.ReactNode;
  scrollDown?: boolean;
};

const ScrollBar = ({ scrollDown, children, ...rest }: TProps) => {
  const [scroller, setScroller] = React.useState<HTMLElement | null>(null);
  const prevChildrenHeight = useRef<number>(0);

  useEffect(() => {
    if (scroller) {
      const currentChildrenHeight = scroller.scrollHeight;
      if (prevChildrenHeight.current !== currentChildrenHeight) {
        prevChildrenHeight.current = currentChildrenHeight;
        if (scrollDown) {
          scroller.scrollTop = currentChildrenHeight;
        }
      }
    }
  }, [scrollDown, scroller, children]);

  return (
    <PerfectScrollbar containerRef={setScrollEl}>{children}</PerfectScrollbar>
  );
};

export default ScrollBar;

Upvotes: 0

Philip DiLeo
Philip DiLeo

Reputation: 11

Another simple option is to add a span to the bottom of your content and then scroll that into view.

Note: I wasn't able to get the containerRef prop on Scrollbar to work properly so this was the next best option.

import React, { useLayoutEffect, useRef } from 'react';
import Scrollbar from 'react-perfect-scrollbar';

...

const MyComp: FunctionComponent = () => {
  const scrollRef = useRef<HTMLSpanElement>();
  
  useLayoutEffect(() => {
    if (scrollRef?.current) {
      scrollRef.current.scrollIntoView();
    }
  });
  
  return (
    <Scrollbar>
      {_.map(data, (d) => renderItem(d))}
      <span ref={scrollRef} />
    </Scrollbar>
  ) 
 }

Upvotes: 1

Artem Korneev
Artem Korneev

Reputation: 66

I hope this sample helps:

import React, { useEffect, useState } from 'react';
import PerfectScrollbar from 'react-perfect-scrollbar';


const Messages = () => {
    const [scrollEl, setScrollEl] = useState();

    useEffect(() => {
      if (scrollEl) {
        scrollEl.scrollTop = 100;
      }
    }, [scrollEl]);

    return (
            <PerfectScrollbar
              containerRef={ref => {
                setScrollEl(ref);
              }}
              >
            {/* ............. */}
              </PerfectScrollbar>
  );
};

export default Messages;

Upvotes: 5

HumanCatfood
HumanCatfood

Reputation: 1001

RPS exposes a method updateScroll on its ref (not to be confused with the "containerRef"!!).

You need to call that after your content has changed.

Upvotes: -2

Related Questions