Skip to content

Commit

Permalink
feat(book-list): rewrote using recyclerlistview
Browse files Browse the repository at this point in the history
  • Loading branch information
isdenmois committed Jan 30, 2021
1 parent c23a8cd commit 67c1261
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 18 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"react-navigation": "4.4.3",
"react-navigation-props-mapper": "1.0.4",
"react-navigation-stack": "2.10.2",
"recyclerlistview": "3.0.5",
"rn-classnames": "1.0.1",
"rxjs": "6.5.3",
"workbox-window": "5.1.4"
Expand Down Expand Up @@ -94,7 +95,7 @@
"babel-plugin-transform-class-properties": "6.24.1",
"clean-webpack-plugin": "3.0.0",
"copy-webpack-plugin": "6.1.1",
"cross-env": "^7.0.3",
"cross-env": "7.0.3",
"cucumber": "6.0.5",
"detox": "17.9.0",
"eslint": "7.16.0",
Expand Down
86 changes: 86 additions & 0 deletions src/components/recycler-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, { createRef, forwardRef, ReactElement, useEffect, useMemo, useState } from 'react';
import { ScrollView, StyleProp, useWindowDimensions, ViewStyle } from 'react-native';
import { BaseScrollView, DataProvider, LayoutProvider, RecyclerListView } from 'recyclerlistview';

interface Props<T> {
data: T[];
rowRenderer: (type: string | number, data: any, index: number, extendedState?: object) => ReactElement;
itemHeight: number;
style?: any;
contentContainerStyle?: StyleProp<ViewStyle>;
ListEmptyComponent?: any;
ListHeaderComponent?: any;
ListFooterComponent?: any;
}

class ExtendedScrollView extends BaseScrollView {
scrollTo(...args) {
this.scrollRef.current?.scrollTo(...args);
}

private scrollRef = createRef<ScrollView>();

render() {
const { children, ListHeaderComponent, ...props } = this.props as any;

return (
<ScrollView {...props} ref={this.scrollRef}>
{ListHeaderComponent}
{children}
</ScrollView>
);
}
}

function RecyclerListComponent<T>(
{ data, itemHeight, ListEmptyComponent, ListFooterComponent, ...props }: Props<T>,
ref,
) {
const provider = useDataProvider(data);
const layoutProvider = useLayoutProvider(itemHeight);

if (!data?.length) {
return ListEmptyComponent || null;
}

return (
<RecyclerListView
ref={ref}
dataProvider={provider}
layoutProvider={layoutProvider}
renderAheadOffset={RecyclerListView.defaultProps.renderAheadOffset + itemHeight}
externalScrollView={ExtendedScrollView}
renderFooter={ListFooterComponent ? () => ListFooterComponent : null}
{...props}
/>
);
}

const createProvider = () => new DataProvider((r1, r2) => r1.id !== r2.id);
function useDataProvider<T>(data: T[]) {
const [provider, setProvider] = useState(createProvider);

useEffect(() => {
setProvider(provider.cloneWithRows(data || []));
}, [data]);

return provider;
}

function useLayoutProvider(itemHeight: number) {
const { width } = useWindowDimensions();

return useMemo(
() =>
new LayoutProvider(
() => 0,
(_, dim) => {
dim.width = width;
dim.height = itemHeight;
},
),
[width, itemHeight],
);
}

export const RecyclerList = forwardRef(RecyclerListComponent);
25 changes: 8 additions & 17 deletions src/screens/book-list/components/book-list.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import _ from 'lodash';
import { FlatList, Text, ViewStyle, TextStyle } from 'react-native';
import { Text, ViewStyle, TextStyle } from 'react-native';
import withObservables from '@nozbe/with-observables';
import { Where } from '@nozbe/watermelondb/QueryDescription';
import { ScrollToTopContext } from 'utils/scroll-to-top';
Expand All @@ -10,6 +10,7 @@ import { database } from 'store';
import Book from 'store/book';
import { BookItem, Button } from 'components';
import { EmptyResult } from 'components/fetcher';
import { RecyclerList } from 'components/recycler-list';
import { BookListFilters } from './book-list-filters';
import { t } from 'services';
import { DynamicStyleSheet } from 'react-native-dynamic';
Expand All @@ -32,7 +33,6 @@ const withBooks: Function = withObservables(['query', 'sort'], ({ query, sort }:
}));

const ITEM_HEIGHT = 116;
const HEADER_HEIGHT = 33;

@withBooks
export class BookList extends React.PureComponent<Props> {
Expand All @@ -47,24 +47,17 @@ export class BookList extends React.PureComponent<Props> {
const books = _.orderBy(this.props.books, this.props.sort.field, order);

return (
<FlatList
contentContainerStyle={ds.dark.scrollContainer}
<RecyclerList
data={books}
initialNumToRender={24}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
rowRenderer={this.rowRenderer}
itemHeight={ITEM_HEIGHT}
contentContainerStyle={ds.dark.scrollContainer}
ListHeaderComponent={this.renderHeader()}
ListFooterComponent={this.renderFooter()}
getItemLayout={this.getItemLayout}
ref={this.context.setScroll}
/>
);
}

private getItemLayout(data, index) {
return { length: ITEM_HEIGHT, offset: HEADER_HEIGHT + ITEM_HEIGHT * index, index };
}

private renderHeader() {
const s = ds[this.props.mode];

Expand All @@ -78,8 +71,8 @@ export class BookList extends React.PureComponent<Props> {
);
}

private renderItem = ({ item }) => {
return <BookItem key={item.id} book={item} cacheThumbnail />;
private rowRenderer = (type, book) => {
return <BookItem book={book} cacheThumbnail />;
};

private renderFooter = () => {
Expand Down Expand Up @@ -125,8 +118,6 @@ export class BookList extends React.PureComponent<Props> {

this.props.onChange(filters);
};

private keyExtractor = book => book.id;
}

const ds = new DynamicStyleSheet({
Expand Down

0 comments on commit 67c1261

Please sign in to comment.