Flipkart / recyclerlistview

High performance listview for React Native and web!
Apache License 2.0
5.25k stars 427 forks source link

Rethinking the "Compat" abstraction (types/reanimated issue) #566

Open mrousavy opened 3 years ago

mrousavy commented 3 years ago

The RecyclerListView component is rather exotic in the react ecosystem, since it does not directly derive from/extend ComponentType<P, S, SS>. Instead, it derives from/extends ComponentCompat<T1, T2, SS>, which itself extends from React.Component<T1, T2, SS>. While this works fine at runtime, it doesn't work when trying to create HoCs or "wrappers" for the RecyclerListView component.

An example for this would be the createAnimatedComponent function from react-native-reanimated, which enables all props and style-props to be animated values/shared values. To maintain type-safety for this function, it is expected to receive a parameter of type ComponentType<P>, which doesn't match the RecyclerListView's type causing the following TypeScript errors:

src/screens/main/home/HomeScreen.tsx:45:71 - error TS2345: Argument of type 'typeof RecyclerListView' is not assignable to parameter of type 'ComponentType<{ style?: StyleProp<object>; }>'.
  Type 'typeof RecyclerListView' is not assignable to type 'ComponentClass<{ style?: StyleProp<object>; }, any>'.
    Types of property 'defaultProps' are incompatible.
      Type '{ canChangeSize: boolean; disableRecycling: boolean; initialOffset: number; initialRenderIndex: number; isHorizontal: boolean; onEndReachedThreshold: number; renderAheadOffset: number; }' has no properties in common with type 'Partial<{ style?: StyleProp<object>; }>'.

45 const ReanimatedRecyclerListView = Reanimated.createAnimatedComponent(RecyclerListView);
                                                                         ~~~~~~~~~~~~~~~~

src/screens/main/home/HomeScreen.tsx:408:11 - error TS2322: Type '{ style: { marginTop: number; flex: number; }; rowRenderer: (type: unknown, data: Post | "ad") => Element | null; dataProvider: DataProvider; layoutProvider: LayoutProvider; ... 5 more ...; scrollEventThrottle: number; }' is not assignable to type 'IntrinsicAttributes & AnimateProps<object, { style?: StyleProp<object>; }> & { children?: ReactNode; }'.
  Property 'rowRenderer' does not exist on type 'IntrinsicAttributes & AnimateProps<object, { style?: StyleProp<object>; }> & { children?: ReactNode; }'.

408           rowRenderer={rowRenderer}
              ~~~~~~~~~~~

In other words: Animated.createAnimatedComponent(RecyclerListView) does not work with TypeScript, so we can't run animations using the onScroll event. (meaning we have "compile"-time errors and no type safety.)

This issue should be a discussion point if we can do something about this in the RecyclerListView types, or if this has to be ignored and casted to unknown, causing all type-safety to be lost. Do we even need the ComponentCompat.ts anymore? Looks like it's not doing anything specific...

likern commented 3 years ago

@mrousavy Am I correct that RecyclerListView can be used with reanimated v2 to be replacement for FlatList, if we ignore type errors?

Can you share your experience in context of reanimated library? Since I came across to this performance complains https://github.com/software-mansion/react-native-reanimated/issues/1219.

Interested is there benefits over FlatList, if using with reanimated and animations.

nandorojo commented 3 years ago

I actually have the same question ^

Does anyone have experience using reanimated with recycler list view? Do you use Animated.createAnimatedComponent and pass an onScroll prop, or do you pass a renderScrollComponent prop with an Animated.ScrollView?

I can circumvent the types issues with ts-expect-error if needed.

likern commented 3 years ago

I actually have the same question ^

Does anyone have experience using reanimated with recycler list view? Do you use Animated.createAnimatedComponent and pass an onScroll prop, or do you pass a renderScrollComponent prop with an Animated.ScrollView?

I can circumvent the types issues with ts-expect-error if needed.

@nandorojo I've experimented with it for react-native-calendar and had working prototype. Unfortunately, that code was not saved somewhere.

I had not seen any performance benefits and, probably, it was even worse than FlatList. At least didn't solve performance issues. So I switched back to just using FlatList, trying to squeeze maximum out of it.

As far as I remember I couldn't not make it working purely on UI. Probably I used renderScrollComponent to provide Animated.ScrollView. Then I used onScroll on that Animated.ScrollView to calculate / run animations on UI / etc.

And you have to report scroll changes back to RecyclerListView, otherwise it wouldn't know about changes happening. So finally I had to call runOnJS(). Probably it ruined the overall idea and performance. Or, at least, I didn't get what expected.

nandorojo commented 3 years ago

Makes sense, good to know. I wonder if there is a good way to interpolate list item style based on the scroll with good performance using the recycler list.

ehsan6sha commented 3 years ago

I actually have the same question ^

Does anyone have experience using reanimated with recycler list view? Do you use Animated.createAnimatedComponent and pass an onScroll prop, or do you pass a renderScrollComponent prop with an Animated.ScrollView?

I can circumvent the types issues with ts-expect-error if needed.

I used RCL with reanimated v2. I used scrollTo from reanimated in an external scrollView of RCL. However, I noticed if reanimated v2 is used inside the recycler elements, the performance drops, so I switched my animations to React Native Animated. You can check out the code here in components/PhotoRender.tsx(implementation of recycler with scrollTo), components/RCL.tsx(just the recycler itself) and components/PhotosChunk.tsx(items in the recycler) https://github.com/functionland/photos

likern commented 3 years ago

@nandorojo Hello ✋🏻 Do you still need interpolating list item style based on the scroll position?