danbovey / react-infinite-scroller

⏬ Infinite scroll component for React in ES6
https://danbovey.uk/react-infinite-scroller/
MIT License
3.29k stars 508 forks source link

isReverse props #92

Open rahmatkruniawan opened 7 years ago

rahmatkruniawan commented 7 years ago

i want use isReverse props but when I rendereing scroll always in top of element. So automatically load more items. How I to prevent autoload and put scroll in bottom when first load.

danbovey commented 7 years ago

I've actually never used the isReverse prop but I imagine it should be used like this:

componentDidMount() {
    if(this._chatbox) {
        this._chatbox.scrollTop = this._chatbox.scrollHeight;
    }
}
<div
    className="chatbox"
    ref={c => this._chatbox = c}
>
    <InfiniteScroll
        ...
        isReverse
    >
        {items}
    </InfiniteScroll>
</div>
rahmatkruniawan commented 7 years ago

I changed to ` componentDidMount(){ this.scrollToBottom(); this.setState({ hasMore : false }) }

componentDidUpdate() {

  if (this.state.first) {
    this.scrollToBottom();
    this.setState({
            hasMore : true,
            first : false
        })  
  }
}

<div style={{height:300, overflow:'auto', border : '1px solid black'}}> <InfiniteScroll ............................. isReverse={true}> {items} <div style={{ float:"left", clear: "both" }} ref={(el) => { this.messagesEnd = el; }} />

    </div>`

But I got more porblem when I scrolling to the top. The scrollbar not bounced like usual when we use without reverse. So when scroll in top they load all data in same time not one by one . layout

tomasz89nowak commented 7 years ago

I was just struggling with this and handle it that way:

goToPrevScroll = (oldScrollHeight) => {
    const list = document.getElementById('messages-list');
    list.scrollTop = list.scrollHeight - oldScrollHeight + list.scrollTop;
}
handleStackMessages = (page) => {
    const list = document.getElementById('messages-list');
    let oldScrollHeight = this.list.scrollHeight;

    getDataFromApi.then(({ data }) => {
        saveYourDataInStateOrStore(data).then(() => {

            // scroll works different on firefox and chrome
            if(isFirefox) {
                // scroll to previous position on firefox
                this.goToPrevScroll(oldScrollHeight);
            } else if(list.scrollTop === 0) {
                // change scrolltop to prevent loading all pages at once on chrome
                this.goToPrevScroll(oldScrollHeight);            
            }
        });
    });
}

sorry for code style, I really don't understand this markdown editor.

EDIT - Fixed code style ^

danielkrich commented 6 years ago

@tomasz89nowak , this code is awesome!! Thank you! One comment though, In my case I wanted to goToPrevScroll(oldScrollHeight) every time the loadMore fires, not only when the scrollTop === 0

Anyway, this helps alot!

@danbovey - I would consider adding something like this in Readme file, because this is the expected functionality every time you want to use isReverse prop

mxstbr commented 5 years ago

That exact functionality is included in the component here https://github.com/CassetteRocks/react-infinite-scroller/blob/54d7b16a257687d6382c04300812f630cee54466/src/InfiniteScroll.js#L50-L57 it just doesn't work at the moment.

mxstbr commented 5 years ago

The reason it does not work is because that runs synchronously, but loadMore (in most cases) runs asynchronously.

The only fix I can think of is requiring loadMore to return a Promise, and then running that code in loadMore.then(() => isReverse ? fixScrollPosition() : null).

gtaylor44 commented 5 years ago

Hmmm... if isReverse is true, the initial scroll position should always start at the bottom (without any hacks needed). Will you accept a merge request or is this intended behaviour?

BenGreybox commented 5 years ago

is the a demo solution for this, cause right now it keeps loading like mad, and scroll bar keeps on top. ( working with the demo files )

flache commented 5 years ago

for me, the functionality in the component works fine (also with an async loadMore function).

However, I had massive problems on iOS Safari due to the fact that when using -webkit-overflow-scrolling: touch it's not possible to set a scrollTop on an element while it still has some of the "bounce". Using this fix worked for me:

if (this.props.isReverse && this.loadMore) { 
   const parentElement = this.getParentElement(this.scrollComponent); 
   parentElement.style['-webkit-overflow-scrolling'] = 'auto';
   parentElement.scrollTop = 
     parentElement.scrollHeight - 
     this.beforeScrollHeight + 
     this.beforeScrollTop; 
   parentElement.style['-webkit-overflow-scrolling'] = 'touch';
   this.loadMore = false;
}
x1nGoo commented 5 years ago

em.. how to to solve this problem? I also have this pro, after loadMore the scrollbar is not correct

ThaddeusJiang commented 5 years ago

me too.

ghost commented 4 years ago

Is this library dead?

duarte-evocorp commented 4 years ago

How to solve this problem?

ghost commented 4 years ago

My solution is:

const [hasMoreItems, setHasMoreItems] = useState(true);
const commentsRef = useRef();

const goToPrevScroll = (oldHeight = 0) => {
    const { current } = commentsRef;

    if (current) {
      current.scrollTop = current.scrollHeight - oldHeight + current.scrollTop;
    }
  };

const handleFetchMessages = async page => {
    const oldHeight = commentsRef.current && commentsRef.current.scrollHeight;

    const { data, headers } = await fetchMessages(page);

    const total = Number(headers['x-total-count']);
    const totalPages = Math.ceil(total / MIN_BATCH_SIZE);

    setHasMoreItems(page < totalPages);
    setMessages(data.reverse().concat(messages));

    if (commentsRef.current && commentsRef.current.scrollTop === 0) {
      goToPrevScroll(oldHeight);
    }
  };

...

<div className="scroll-wrap" ref={commentsRef}>
  <InfiniteScroll
          pageStart={0}
          initialLoad
          loadMore={handleFetchMessages}
          hasMore={hasMoreItems}
          loader={<Loading />}
          useWindow={false}
          isReverse
          threshold={150}
        >
          {messages.map(comment => (
            ...
          ))}
  </InfiniteScroll>
</div>
oran1248 commented 4 years ago

@danbovey Why we need to use hacks for such a common use? I think i'm going to abandon this library because of this. that's a shame...

gtaylor44 commented 4 years ago

I also abandoned when I realised this functionality was missing.

I ended up using react-perfect-scrollbar with an adhoc solution.

https://www.npmjs.com/package/react-perfect-scrollbar

kindmind commented 4 years ago

I'm able to get this mostly working in isReverse mode but I've spent the last two days running into issues due to scrolling to the top too fast. The scrollbar gets "stuck" at top and future elements being loaded don't offset the scroll position as they should. Very frustrating. Abandoning this package and using react-waypoints and apollo for reverse infinite scrolling.

angelitolm commented 4 years ago

My solution is:

const [hasMoreItems, setHasMoreItems] = useState(true);
const commentsRef = useRef();

const goToPrevScroll = (oldHeight = 0) => {
    const { current } = commentsRef;

    if (current) {
      current.scrollTop = current.scrollHeight - oldHeight + current.scrollTop;
    }
  };

const handleFetchMessages = async page => {
    const oldHeight = commentsRef.current && commentsRef.current.scrollHeight;

    const { data, headers } = await fetchMessages(page);

    const total = Number(headers['x-total-count']);
    const totalPages = Math.ceil(total / MIN_BATCH_SIZE);

    setHasMoreItems(page < totalPages);
    setMessages(data.reverse().concat(messages));

    if (commentsRef.current && commentsRef.current.scrollTop === 0) {
      goToPrevScroll(oldHeight);
    }
  };

...

<div className="scroll-wrap" ref={commentsRef}>
  <InfiniteScroll
          pageStart={0}
          initialLoad
          loadMore={handleFetchMessages}
          hasMore={hasMoreItems}
          loader={<Loading />}
          useWindow={false}
          isReverse
          threshold={150}
        >
          {messages.map(comment => (
            ...
          ))}
  </InfiniteScroll>
</div>
angelitolm commented 4 years ago

Hello I would like to know how I could implement this same code but already receiving the data by props instead of doing a fetch

duarte-evocorp commented 4 years ago

@gtaylor44, does this new library have a reverse scroll option?

gtaylor44 commented 4 years ago

React-perfect-scrollbar has props you can hook into such as: onYReachStart onScrollUp onYReachEnd

But you will still have to deal with issues that @kindmind has mentioned such as "The scrollbar gets stuck" when the user drags scrollbar to the top with mouse. You can work around the "stuck scrollbar" by having an interval that checks if scrollTop = 0 and scrollBy 1 (for top) and -1 (for bottom).

Here are some screen captures for inspiration with react-perfect-scrollbar. App attempts to load more records before user actually hits the top of scrollbar by using combination of onScrollUp prop and scrollTop position.

screen-capture.zip

mouse-stuck-problem.zip