Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FlatList automatically scrolls after adding new items #25239

Open
SiSa68 opened this issue Jun 12, 2019 · 110 comments
Open

FlatList automatically scrolls after adding new items #25239

SiSa68 opened this issue Jun 12, 2019 · 110 comments

Comments

@SiSa68
Copy link

@SiSa68 SiSa68 commented Jun 12, 2019

FlatList automatically scrolls after change data and adds new data in the front or middle of the data list.
It only works correctly when adds new item to the end of list
I checked scroll offset and understand that FlatList scrolls to keep the latest content Y offset
I mean when the content size changes, the latest Y offset now is not where the user was before! but FlatList scrolls to it

React Native version:
react-native: 0.59.4
System:
OS: Windows 7
CPU: (2) x64 Intel(R) Pentium(R) CPU G4400 @ 3.30GHz
Memory: 310.09 MB / 3.87 GB
Binaries:
Yarn: 1.15.2 - C:\Program Files (x86)\Yarn\bin\yarn.CMD
npm: 6.9.0 - C:\Program Files\nodejs\npm.CMD
IDEs:
Android Studio: Version 3.2.0.0 AI-181.5540.7.32.5056338

Describe what you expected to happen:

It shouldn't scroll to new position when I add new items to list, and should keep latest position where user was

Example code:

<FlatList
inverted
style={{flex: 1}}
data={this.data}
keyExtractor={(item, index) => item.id}
renderItem={this.renderItem}
ref={ref => this.flatList = ref}
/>

Code that adds new items to list:

this.data = [ ...newItems, ...this.data ];

@sgtpepper43
Copy link

@sgtpepper43 sgtpepper43 commented Jun 20, 2019

If you only need this fixed in ios, there's an undocumented prop maintainVisibleContentPosition on ScrollView that ought to do what you want. Unfortunately it doesn't work on android. I've been trying to solve this problem for the last two days in my own code and it's a freaking nightmare.

Loading

@SiSa68
Copy link
Author

@SiSa68 SiSa68 commented Jun 22, 2019

@sgtpepper43 Thank you for participate
I need in both android and iOS.
I find workaround by keep latest y offset with onScroll and also save content height before and after adding new items with onContentSizeChange and calculate the difference of content height, and set new y offset to the latest y offset + content height difference!

But its not a good way, because it has UX problem. Firstly it scrolls down and after that scrolls back to the latest position
And it's not working when I want to load my list item from realm database

There should be a simpler way to do this and I don't understand why there is not?!

Loading

@sgtpepper43
Copy link

@sgtpepper43 sgtpepper43 commented Jun 22, 2019

That's the same point I'm at 😩 I was going to try and fix it, but it's taken longer to try to get the Android code building and running in our app then writing the fix itself will take... I still haven't gotten it to compile. So I'm giving up on that.

Loading

@SudoPlz
Copy link
Contributor

@SudoPlz SudoPlz commented Jul 1, 2019

fyi, I've asked the same thing here https://stackoverflow.com/questions/56707931/prepending-data-to-a-flatlist-always-shows-the-first-child/56837491#56837491

No actual fixes so far.

Leaving that here for future reference.

Loading

@winardis
Copy link

@winardis winardis commented Jul 18, 2019

having the same issue here on trying to prepend data to my datasource,
i found "maintainVisibleContentPosition" but that only works for IOS, Android is not supported
have you guys found any other solutions on this without manually scrollToIndex of course

Loading

@kirillpisarev
Copy link

@kirillpisarev kirillpisarev commented Jul 22, 2019

I was afraid that there is no straightforward way to achieve this, but at the moment after a couple of days of trying to implement this feature, it looks like RN is not an appropriate tool to implement chat apps.

Loading

@SiSa68
Copy link
Author

@SiSa68 SiSa68 commented Jul 22, 2019

I can not believe that they are not support this feature at all!!!
I found RecyclerListView
It might solve this problem, But I didn't currently have the opportunity to test it
If someone has an opportunity, please test and give the result to the React Native community!

Loading

@jmacioszek
Copy link

@jmacioszek jmacioszek commented Jul 31, 2019

That's an issue for me as well, would be nice to have this working with SectionList.

Loading

@AVert
Copy link

@AVert AVert commented Aug 14, 2019

Have same problem Guys ((
maintainVisibleContentPosition not work on Android

Loading

@SudoPlz
Copy link
Contributor

@SudoPlz SudoPlz commented Aug 14, 2019

I created a ticket in Canny, please upvote, this has to get fixed soon https://react-native.canny.io/feature-requests/p/allow-prepending-new-items-to-flatlists-without-side-effects

Loading

@AVert
Copy link

@AVert AVert commented Aug 26, 2019

@sahrens
Could you please triage this? This is a regression/bug, right? If so, any advice how to fix it? Thanks!

Loading

@AVert
Copy link

@AVert AVert commented Aug 28, 2019

i check this same way not work

I can not believe that they are not support this future at all!!!
I found RecyclerListView
It might solve this problem, But I didn't currently have the opportunity to test it
If someone has an opportunity, please test and give the result to the React Native community!

Loading

@AVert
Copy link

@AVert AVert commented Aug 28, 2019

I can not believe that they are not support this future at all!!!
I found RecyclerListView
It might solve this problem, But I didn't currently have the opportunity to test it
If someone has an opportunity, please test and give the result to the React Native community!

@naqvitalha can you change you component to mak it work as we need?

Loading

@AVert
Copy link

@AVert AVert commented Aug 29, 2019

I can not believe that they are not support this future at all!!!
I found RecyclerListView
It might solve this problem, But I didn't currently have the opportunity to test it
If someone has an opportunity, please test and give the result to the React Native community!

I try this - no way

Loading

@woody-riddle
Copy link

@woody-riddle woody-riddle commented Aug 30, 2019

@AVert
Thanks for your try.
If you fix it, let's talk how you use that instead of ScrollView and maintainVisibleContentPosition.
I will try that too.
Best wishes for you.

Loading

@dmkerfont
Copy link

@dmkerfont dmkerfont commented Oct 10, 2019

Clarification: I'm creating a chat app which will append pages of messages as user scrolls up, and prepend newly received messages to the bottom.

My solution using an inverted flatlist was to not render newly received messages at the bottom, unless/until the user is scrolled to the bottom.
Here's how I achieved that:

  1. Add following component state variables

    • isScrolledBottom: boolean
    • newMessageList[]
  2. define a threshold for deciding if we are scrolled to bottom of list
    myThreshold = 100 pixels

  3. Create a method for handling onBottomReached while scrolling
    onBottomReached()
    {
    if(newMessageList > 0){
    - append them to my flatlist data source
    - remove them from my newMessageList in component state
    - scroll to bottom
    }
    }

  4. flatlist onScroll:
    if (crossed 100px threshold, in either direction)

    • if at bottom, then call onBottomReached()
    • set state.isScrolledToBottom = T/F
  5. New message received:
    if(at bottom){
    append to flatlist data source
    }
    else{
    append to newMessageList
    }

This gave me the best user experience, since maintainVisibleContentPosition does not work on android, so I did not use it at all.

Loading

@SudoPlz
Copy link
Contributor

@SudoPlz SudoPlz commented Oct 10, 2019

@dmkerfont I don't really get it, what problem does this solve? It looks like you're not "prepending" data, looks like you're only appending.

Isn't the issue only apparent when someone tries to prepend data (and thus get's scrolled on a wrong date?)
Have I miss-understood something?

Thanks

Loading

@dmkerfont
Copy link

@dmkerfont dmkerfont commented Oct 10, 2019

@SudoPlz
With an inverted flatlist. The 'end' of the list is actually the top, thus scrolling up will continue to add older pages ( appending to the end ).

When a new message comes in, prepending it to the front of the list will cause the data shifting issue as the indexes are reset.

Instead of prepending every message that comes in, I'm checking if the user is at/near the bottom or if they are scrolled up a significant distance.

If at bottom, then prepend and scroll down.
Else, build a queue of messages to prepend.

If user reaches bottom, through a manual scroll or pressing a button like a 'New Message' indicator, then simply prepend the queued messages.

I have no experience with native code so I have no desire to implement maintainVisibleContentPosition at this time, but this is a decent workaround.

Loading

@SudoPlz
Copy link
Contributor

@SudoPlz SudoPlz commented Oct 10, 2019

@dmkerfont interesting, is it too much to ask for some visual example? I've taken the time to replicate what you said with numbers below, does it look right?

Example:

Inverted flatlist

[3]
[2] <-- Index 2 / User sees 2
[1]
[0]

user manually scrolls to the bottom, then:

[3]
[2] 
[1]
[0]<-- Index 0 / User sees 0

New items [4][5][6] get prepended:

[3]
[2] 
[1]
[0]
[4]
[5]
[6]<-- Index 0 / User sees 6

so the user ends up seeing [6] now (which is now index 0) .

Then as an extra step the user get's scrolled to 0 again?

  • Isn't that a problem? Shouldn't the user see [4] instead? (index 2) ? since it's the next item after [0] ?
  • Also another question, does appending (adding items after [3]) work fine using an inverted list?

I appreciate your time, thanks.

Loading

@dmkerfont
Copy link

@dmkerfont dmkerfont commented Oct 10, 2019

@SudoPlz

Isn't that a problem? Shouldn't the user see [4] instead? (index 2) ? since it's the next item after [0] ?

Shoot... You are right. If I'm scrolled up (and want my scroll position preserved), then I build my list of new messages.
Once I scroll down manually, when I hit the bottom and the list [4][5][6] is prepended, I'm suddenly staring at item [6] instead of [4]. This means that the issue still exists, but you will only notice it if you are prepending more than 1 or 2 items to the list at a time while scrolling.

Perhaps this alternative isn't worth all the effort after all. :(

Also another question, does appending (adding items after [3]) work fine using an inverted list?

If you've done any paging, you will have noticed that adding items to the end of the list ( bottom for regular flatlist ) works as expected. This is the same when appending (top) to an inverted flatlist.

Loading

@SudoPlz
Copy link
Contributor

@SudoPlz SudoPlz commented Oct 10, 2019

Ok cool, please let me know if you find a way to make the user see [4] instead (the next item).

Loading

@jesster2k10
Copy link

@jesster2k10 jesster2k10 commented Nov 17, 2019

What’s the story with this issue? Every time the data source on the flat list changes, say once new data is fetched or prended to the list it resets the scroll position. I’m building some sort of user feed, so I need to be able to update the data source without messing up the users scroll position.

Is this possible at all with react native? The maintainVisibleContentPosition prop seems to do nothing for me.

Loading

@jakovB777
Copy link

@jakovB777 jakovB777 commented Nov 25, 2019

Edit: As Sisa68 noted, I misunderstood the struggle here. Mb

Loading

@SiSa68
Copy link
Author

@SiSa68 SiSa68 commented Nov 26, 2019

@jakovB777 I think our case are different from yours!
We want to add item to both side of list, not only push on back.
There is no problem when you have one side list with FlatList

Loading

@isuhar
Copy link

@isuhar isuhar commented Nov 26, 2019

Same problem =(( We add new items to the start of array and position is still 0

Loading

@toddrimes
Copy link

@toddrimes toddrimes commented Dec 6, 2019

This works for me:

Screen Shot 2019-12-06 at 9 44 51 AM

Loading

@meosieudang
Copy link

@meosieudang meosieudang commented Dec 24, 2019

anyone fix it?

Loading

@Donhv
Copy link

@Donhv Donhv commented Dec 25, 2019

Loading

@akhil-geekyants
Copy link

@akhil-geekyants akhil-geekyants commented Oct 19, 2020

Please fix this.

Loading

@ghasemikasra39
Copy link

@ghasemikasra39 ghasemikasra39 commented Nov 20, 2020

I tested maintainVisibleContentPosition on iOS. But this property only works if you append data. if you prepend data, it does not work.

Loading

@ghasemikasra39
Copy link

@ghasemikasra39 ghasemikasra39 commented Nov 20, 2020

Currently, the PR Support for ScrollView.maintainVisibleContentPosition on Android is open.
I brought the RN that built this branch into my app.
And it is working as expected!

However, the same problem as this occurs in my app both ios and android.

// package.json

  "react-native": "github:abeyuya/react-native#patch-maintainVisibleContentPosition",

The issue is not whether is works on iOS or android. Please don't get confused by this. The issue here is:
Even on iOS devices that the property maintainVisibleContentPosition is available, there is this issue:
prepending data will change the visible content position. This is not the expected behaviour.

Loading

@divonelnc
Copy link

@divonelnc divonelnc commented Nov 20, 2020

I noticed that maintainVisibleContentPosition does not work if you prepend data while you are at a scroll position of 0 (all the way to the top). It does work if you are prepending while not a the top. Even scroll Y of 1 would be enough. Tested at least on web: See snack.

Furthermore, maintainVisibleContentPosition does not work the first time you prepend, no matter your scroll position. It does work afterward. See #19621

Loading

@ghasemikasra39
Copy link

@ghasemikasra39 ghasemikasra39 commented Nov 20, 2020

I noticed that maintainVisibleContentPosition does not work if you prepend data while you are at a scroll position of 0 (all the way to the top). It does work if you are prepending while not a the top. Even scroll Y of 1 would be enough. Tested at least on web: See snack.

Furthermore, maintainVisibleContentPosition does not work the first time you prepend, no matter your scroll position. It does work afterward. See #19621

It's true. I tested this in our React Native app and it's working when I have passed the very first item in the list.
I am using latest version React Native 0.63.3 and this issue is still there. Then why #19621 issue is closed!

Loading

@thanhdevapp
Copy link

@thanhdevapp thanhdevapp commented Nov 24, 2020

same problem if flatlist inside tabview

Loading

@MohamedSR
Copy link

@MohamedSR MohamedSR commented Nov 27, 2020

Okey so after 10000 libraries and hacks i arrived to accept that the lazy loading way with pagination does not work with RN Flatlist instead there are two props according to the documentation are used to optimize Flatlist performance maxToRenderPerBatch and initialNumToRender

Optimizing Flatlist Configuration

Loading

@vishalnarkhede
Copy link

@vishalnarkhede vishalnarkhede commented Dec 9, 2020

If someone is looking for solution regarding android support for maintainVisibleContentPosition on android, please check - https://github.com/GetStream/flat-list-mvcp

Reference - #29466 (comment)

Loading

@VictorioMolina
Copy link

@VictorioMolina VictorioMolina commented Jan 22, 2021

Same problem here #30775

Loading

@VictorioMolina
Copy link

@VictorioMolina VictorioMolina commented Jan 22, 2021

As I am implementing a bidirectional chat screen, the only way to solve this issue (a workaround) has been to use

inverted={true} invertStickyHeaders={true} stickySectionHeadersEnabled={true}

And think in the opposite direction when inserting sections and messages, in order to have the most recent message sections and messages at the end of the inverted list (at the end of the device screen).

Thinking backwards is more complex, but it's the only thing I can think of for now until the bug is solved.

The only catch, SectionList's inverted headers don't work correctly ... At least not completely. #14520

But you can get a good behaviour using these props for the inverted section list:

inverted={true} renderSectionFooter={renderSectionFooter}

Instead of rendering the chat date at the sectionHeader, I am rendering it at the footer of the section... Looking for a stickySectionFootersEnabled prop.

But... there is a medium post to solve this issue with sticky section footers https://medium.com/@smali.kazmi/react-native-sticky-date-like-whatsapp-using-inverted-sectionlist-c141f5933d55

After doing this tricky coding, I am getting a good behaviour in both platforms (Android and iOS)

Loading

@Leonamquintao
Copy link

@Leonamquintao Leonamquintao commented Feb 11, 2021

am I still facing this problem, any solution? (11/02/2021)

it's very frustrating ...
I have a parent component that fetches the data with pagination and distributes it to the children ...
but when I call onReachEnd => loadMore () the child component goes to the top 😖

here the UI
print

Loading

@xmflsct
Copy link

@xmflsct xmflsct commented Feb 11, 2021

I followed #25239 (comment) advice, use ref to scroll flatlist for 1px, before adding new items, which works for pull for refresh. But adding new items after scrolled, it is working well.

Also, I notice that, if the added items are long, or taking much height, maintaining scrolling position breaks. So I have to gradually adding items.

Loading

@vishalnarkhede
Copy link

@vishalnarkhede vishalnarkhede commented Feb 12, 2021

Loading

@theprdv
Copy link

@theprdv theprdv commented Feb 12, 2021

@SudoPlz any updates?

Loading

@Leonamquintao
Copy link

@Leonamquintao Leonamquintao commented Feb 12, 2021

@Leonamquintao Did you try flat-list-mvcp?

I'll take a look @vishalnarkhede

Loading

@rgbedin
Copy link

@rgbedin rgbedin commented Mar 9, 2021

Are there any updates on this fix? Thanks!

Loading

okpalaChidiebere added a commit to okpalaChidiebere/serverless-capstone-project that referenced this issue Mar 13, 2021
The idea here is to open a websocet connection after the user logs in so that they can get notifications

Better ways to implement websocket is to wrap it inside a React Context called 'IndexController' that will wrap the App containter
eg here https://www.pluralsight.com/guides/using-web-sockets-in-your-reactredux-app

When the user gets notiications in a screen that connects to a redux store that gets updated, i wanted the scrollView to be thesame. More resources on how i did that here
https://medium.com/@mikolajkocieda/react-hooks-keep-scroll-position-during-change-list-views-cd86463f9629
https://kempsterrrr.xyz/articles/handling-scroll-events-in-react

For React Native on maintianing scroll position after re-render
https://stackoverflow.com/questions/33748967/change-scrolltop-in-reactjs/55184755
facebook/react-native#25239

More on useMemo
https://kentcdodds.com/blog/usememo-and-usecallback

More on useRef Typescript
https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315
@johnbonds
Copy link

@johnbonds johnbonds commented Mar 16, 2021

Pretty please... with sugar on top... and a cherry... I'm talkin like a full banana split here...

Loading

@TreoMang
Copy link

@TreoMang TreoMang commented Apr 1, 2021

I think we can try resolved this problem on Android follow this https://expo.canny.io/feature-requests/p/android-maintainvisiblecontentposition-on-flatlist. He write a library for Android (native). I haven't try yet. I will report result for some can not try late.

Loading

@TreoMang
Copy link

@TreoMang TreoMang commented Apr 2, 2021

I think we can try resolved this problem on Android follow this https://expo.canny.io/feature-requests/p/android-maintainvisiblecontentposition-on-flatlist. He write a library for Android (native). I haven't try yet. I will report result for some can not try late.

I tried and success. But I have Animated for Flatlist, so now I get some effect side from top to bottom to load new list.

Loading

@vtquy98
Copy link

@vtquy98 vtquy98 commented Jul 12, 2021

My solution:

<ScrollView
  ref={scrollViewRef}
  style={styles.container}
  showsVerticalScrollIndicator={false}
  showsHorizontalScrollIndicator={false}
  onContentSizeChange={() =>
    scrollViewRef.current.scrollToEnd({ animated: true })
  }
>
  <FlatList
    data={post.comments}
    contentContainerStyle={{
      backgroundColor: alpha(colors.text, 0.1).toString(),
    }}
    renderItem={renderCommentItem}
    keyExtractor={(item) => item.id}
    ItemSeparatorComponent={() => <Atoms.Spacer size={5} />}
  />
</ScrollView>

Loading

@Rutvik17
Copy link

@Rutvik17 Rutvik17 commented Aug 4, 2021

This is one of the biggest frustrations of using a FlatList and that it is not very ideal for a pagination behavior when we want to append and prepend data. We have a chat app in react-native using FlatList and we have this big limitation when it comes to prepending data to the FlatList to support some features like message searching and photo gallery.

After many dead ends I was able to come up with a solution that meets the need and does not hurt the UI experience at the same time. This is only applicable if you have fixed content size. e.g Photo library in a chat app where the width of the photos are fixed.

Let's say you have 100 Photos and the user clicks on the 50th photo in the list, this mean when we render the photo library FlatList we need to be scrolled to the 50th photo the user tapped on and we need to add photos before and after the 50th the photo meaning ....45, 46, 47, 48, 49 | 50 | 51, 52, 53, 54, 55.... this can be achieved without loosing the scroll position and never having to prepend photos.

When initializing the PhotosArray we get all the photos till the 50th including 50th so from 0 to 50 meaning the photo user tapped on is the last photo in the array.

we shall be using an inverted flatlist so the onEndReached callback is called when its scrolled all the way to the top and prepending is when scrolled all the way to the bottom`

We are going to use the FlatListRef to scroll to the index the user tapped on. Meaning when the FlatList is rendered the 50th photo in the photos array is displayed

// create loading and photos local state
const [loading, setLoading] = React.useState(true);
const [photos, setPhotos] = React.useState<Array<Photo>>([]);
const flatListRef = React.useRef() as React.MutableRefObject<FlatList<Photo>>;

// Here initialize your photos and add photos till the photo user tapped on.
React.useEffect(() => {
   setPhotos([1,2,3,4......47,48,49,50]);
}, [])

React.useEffect(() => {

     if (photos && loading) {
       const indexToScrollTo = photos.length - 1;

       if (indexToScrollTo > -1) {

          if (Platform.OS === 'android') {
           flatListRef.current.scrollToIndex({animated:false, index: scrollToIndex});
          }

          if (Platform.OS === 'ios') {

            // apparently this does not work smoothly without a timeout on ios
            setTimeout(() => {

             flatListRef.current.scrollToIndex({animated:false, index: scrollToIndex});
              setTimeout(() => {

               //here append the photos after 50, and since append will maintain the 
                flatlist position you dont have to worry if you append 10 at a time 
                or all of the remaining photos since flatList will render the lazily it shouldn't matter.

               appendPhotos();
               setLoading(false);

              }, 200) 
              // this second timeout will give some time for the flatList to
              finish scrolling to the index specified before appending and
              I have tested this a bunch of times and it seems to be working great.
            }, 0)
          }
       }
     }
}, [photos, loading])

// Now for the FlatList,
<FlatList data={photos}
               ref={flatListRef}
               key={item => item.id}
               inverted
               pagingEnabled
               horizontal={true}
               onEndReached={appendPhotos}
               scrollEnabled={!loading} // we disable the scrolling because we dont want the user to 
                                                          to scroll while the ### INITIAL append is still in progress.
              getItemLayout={(data, index) => (
                 {length: FIXED_WIDTH, offset: FIXED_WIDTH * index, index}
              )} // this is a must as scroll to index wont work without this
              // can add more props
/>

// The width of the photos is fixed to the width of the device since its a horizontal photo sliding FlatList.

// Important Notes

  1. iOS requires a setTimeout inorder for it to scroll to the specified index without displaying the first index or even breaking.
  2. In the second setTimeOut atleast append a few more items inorder to scroll towards the left otherwise on the first scroll there will be no photos and they will append after the first scroll.
  3. You should disable the scrolling until the scroll and append is finished this take about 200ms based on out setTimeOut

Feel free to correct me or please write down a better working approach if you have one without setTimeOut.

Loading

@dolphinflow86
Copy link

@dolphinflow86 dolphinflow86 commented Aug 17, 2021

Any proper solution yet with dynamic height rows?
I'm curious if I switch to another framework or to native, would it have no problem of prepending issue?

Loading

@brusatimatias
Copy link

@brusatimatias brusatimatias commented Sep 10, 2021

This partially solves my problem. It's the only way I found when updating an article, it should work when you add an item as well.

const flatListRef = useRef();
const scrollPositionRef = useRef();

<FlatList
  ref={flatListRef}
  data={data}
  renderItem={(item) => renderItem(item)}
  keyExtractor={(item) => item.id.toString()}
  onViewableItemsChanged={() => {
    flatListRef.current.scrollToOffset({ animated: false, offset: scrollPositionRef.current })
  }}
  onScroll={(event) => scrollPositionRef.current = event.nativeEvent.contentOffset.y}
/>

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet