-
Notifications
You must be signed in to change notification settings - Fork 27
WIP: Web support #16
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
base: main
Are you sure you want to change the base?
WIP: Web support #16
Conversation
vishalnarkhede
commented
May 24, 2021
•
edited
Loading
edited
- Using react-virtuoso to power web support for infinite scrollable list, keeping the same API as FlatList
- https://codesandbox.io/s/rnw-support-infinite-scroll-3i4q3?file=/src/MessageListExample.tsx
src/BidirectionalFlatList.tsx
Outdated
behavior: animated ? 'smooth' : 'auto', | ||
index: 0, | ||
}); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be switched to
const index = inverted ? 0 : vData.length - 1;
virtuosoRef.current?.scrollToIndex({
behavior: animated ? 'smooth' : 'auto',
index,
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks , I will check it out. Planning to publish beta on this next week :) Please let me know if you find more things.
PR needs some testing and some cleanup which will happen early next week :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried this out over the weekend and put some thoughts here
No hard feelings, but at the moment this web
implementation is noticeably slower than the web implementation from react-native-web
Flatlist
I've tried playing around with overscan
but it doesn't seem to affect the issue
It looks like Virtuoso is the bottleneck though I'm not 100% certain
"react-native": "0.63.4", | ||
"react-native-builder-bob": "^0.17.1", | ||
"react-virtuoso": "^1.6.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
react-virtuoso
should probably be listed in dependencies
or peerDependencies
src/BidirectionalFlatList.tsx
Outdated
() => ({ | ||
flashScrollIndicators: () => null, | ||
getNativeScrollRef: () => null, | ||
getScrollableNode: () => null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can return scrollerRef.current
- getScrollableNode: () => scrollerRef.current
Unfortunately it will be available on the 2nd render, so the parent component that uses BidirectionalFlatList
won't have it during componentDidMount
or useEffect(() => {}, [])
With the original FlatList
getScrollableNode
would return the node for componentDidMount
src/BidirectionalFlatList.tsx
Outdated
<Virtuoso<T> | ||
components={{ | ||
Footer, | ||
Header, | ||
}} | ||
firstItemIndex={firstItemIndex.current} | ||
initialTopMostItemIndex={ | ||
inverted | ||
? vData.length - 1 - initialScrollIndex | ||
: initialScrollIndex | ||
} | ||
itemContent={itemContent} | ||
onScroll={handleScroll} | ||
overscan={300} | ||
ref={virtuosoRef} | ||
// @ts-ignore | ||
scrollerRef={(ref) => (scrollerRef.current = ref)} | ||
totalCount={vData.length} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This portion is pretty much repeated and can be extracted to a const
const VirtuozoNode = (
<Virtuoso<T>
components={{}}
...
/>
);
if (!onRefresh) {
return VirtuozoNode;
}
return (
<Animated.View>
<Animated.View>
<ActivityIndicator />
</Animated.View>
{VirtuozoNode}
</Animated.View>
)
src/BidirectionalFlatList.tsx
Outdated
<View style={styles.indicatorContainer}> | ||
<ActivityIndicator size={'small'} color={activityIndicatorColor} /> | ||
</View> | ||
<Virtuoso<T> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FlatList
has getItemLayout
prop to aid rendering
Virtuoso
has itemSize
but it's called with an html element: https://virtuoso.dev/virtuoso-api-reference/ (see itemSize
)
I don't see a way to make use of getItemLayout
with Virtuoso
FlatList.getItemLayout
-> is called withdata, index
and returns{ length, offset, index }
Virtuoso.itemSize
-> is called withdomNode
and returns anumber
(height or width of the item)- does this mean that Virtuoso have to first render the items and then update the size ?
For web it might be impossible to implement getItemLayout
when Virtuoso
is used for virtualization
Other libraries like react-virtual provide a compatible interface
You can do something like:
estimateSize = index => {
const size = props.getItemLayout(props.data, index);
return size.length;
}
src/BidirectionalFlatList.tsx
Outdated
} | ||
itemContent={itemContent} | ||
onScroll={handleScroll} | ||
overscan={300} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be something like
overscan={(scrollerRef.current?.offsetHeight * props.windowSize) || 300}
As per https://reactnative.dev/docs/virtualizedlist#windowsize this is how many screens of data to pre-render
And per https://virtuoso.dev/virtuoso-api-reference/ overscan
is in px
Not sure if windowSize
has to be halved though
- for 21 react-native FlatList would add 10 pages up and 10 down
- would Virtuoso do the same or add 21 pages up and 21 pages down?
src/BidirectionalFlatList.tsx
Outdated
}, [onRefresh]); | ||
|
||
if (!vData?.length || vData?.length === 0) { | ||
return <ListEmptyComponent />; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that this does not have a default and crashes when vData
is empty
Perhaps it can just return null
when ListEmptyComponent
is not defined
or have a default ListEmptyComponent
const startReached = async () => { | ||
if ( | ||
!onStartReached || | ||
prependingItems.current || | ||
onStartReachedInProgress || | ||
(inverted && !onEndReached) || | ||
(!inverted && !onStartReached) | ||
) { | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason both startReached
and endReached
are not always triggered, even when there is more data to be loaded
I think it has to do with the count of items rendered
For some chats scrolling to the past (on inverted list) triggers onEndReached
and loads older messages, for others it isn't getting picked up
We're using an inverted
list so that's where I've test the most, but I did swap it for non-inverted and it seemed it worked better
Hey @kidroca can you share some screen-recording? Just to understand !! |
…Stream/react-native-bidirectional-infinite-scroll into vishal/web-support-onrefresh
e685877
to
c1f1876
Compare
@vishalnarkhede do you plan to finish web support? Is this package still being developed? |