Skip to content

Commit

Permalink
Merge branch 'Flipkart:master' into fix-rtl
Browse files Browse the repository at this point in the history
  • Loading branch information
Matergi committed Jun 9, 2022
2 parents 1b9d2ea + 016b1f7 commit 1a316ad
Show file tree
Hide file tree
Showing 17 changed files with 379 additions and 140 deletions.
10 changes: 5 additions & 5 deletions README.md
Expand Up @@ -35,6 +35,7 @@ that does not compromise performance or memory efficiency.
Apart from all performance benefits RecyclerListView comes with great features out of the box:
- Cross Platform, works on Web
- Supports staggered grid layouts
- Supports variable height items even if dimensions cannot be predetermined (prop - `forceNonDeterministicRendering`)
- Instant layout switching like going from GridView to ListView and vice versa
- End reach detections
- Horizontal Mode
Expand All @@ -44,7 +45,6 @@ Apart from all performance benefits RecyclerListView comes with great features o
- Reflow support on container size change with first visible item preservation
- Scroll position preservation
- Window scrolling support for web
- Non deterministic rendering mode on demand (height cannot be determined before rendering)
- (New) ItemAnimator interface added, customize to your will how RLV handles layout changes. Allows you to modify animations that move cells. You can do things like smoothly move an item to a new position when height of one of the cells has changed.
- (New) Stable Id support, ability to associate a stable id with an item. Will enable beautiful add/remove animations and optimize re-renders when DataProvider is updated.
- (New) Sticky recycler items that stick to either the top or bottom.
Expand All @@ -57,8 +57,7 @@ is a way to look at the data and compute height upfront so that RecyclerListView
You can still do all sorts of GridViews and ListViews with different types of items which are all recycled in optimal ways. Type based recycling is very easy
to do and comes out of the box.

In case you really need non deterministic rendering set `forceNonDeterministicRendering` prop to true on RecyclerListView. This increases layout thrashing and thus, will
not be as fast.
In case you cannot determine heights of items in advance just set `forceNonDeterministicRendering` prop to true on RecyclerListView. Now, it will treat given dimensions as estimates and let items resize. Try to give good estimates to improve experience.


## Demo
Expand All @@ -83,14 +82,15 @@ not be as fast.
| dataProvider | Yes | DataProvider | Constructor function the defines the data for each element |
| contextProvider | No | ContextProvider | Used to maintain scroll position in case view gets destroyed, which often happens with back navigation |
| rowRenderer | Yes | (type: string \| number, data: any, index: number) => JSX.Element \| JSX.Element[] \| null | Method that returns react component to be rendered. You get the type, data, index and extendedState of the view in the callback |
| initialOffset | No | number | Initial offset you want to start rendering from; This is very useful if you want to maintan scroll context across pages. |
| initialOffset | No | number | Initial offset you want to start rendering from; This is very useful if you want to maintain scroll context across pages. |
| renderAheadOffset | No | number | specify how many pixels in advance you want views to be rendered. Increasing this value can help reduce blanks (if any). However, keeping this as low as possible should be the intent. Higher values also increase re-render compute |
| isHorizontal | No | boolean | If true, the list will operate horizontally rather than vertically |
| onScroll | No | rawEvent: ScrollEvent, offsetX: number, offsetY: number) => void | On scroll callback function that executes as a user scrolls |
| onRecreate | No | (params: OnRecreateParams) => void | callback function that gets executed when recreating the recycler view from context provider |
| externalScrollView | No | { new (props: ScrollViewDefaultProps): BaseScrollView } | Use this to pass your on implementation of BaseScrollView |
| onEndReached | No | () => void | Callback function executed when the end of the view is hit (minus onEndThreshold if defined) |
| onEndReachedThreshold | No | number | Specify how many pixels in advance for the onEndReached callback |
| onEndReachedThresholdRelative | No | number | Specify how far from the end (in units of visible length of the list) the bottom edge of the list must be from the end of the content to trigger the onEndReached callback |
| onVisibleIndicesChanged | No | TOnItemStatusChanged | Provides visible index; helpful in sending impression events |
| onVisibleIndexesChanged | No | TOnItemStatusChanged | (Deprecated in 2.0 beta) Provides visible index; helpful in sending impression events |
| renderFooter | No | () => JSX.Element \| JSX.Element[] \| null | Provide this method if you want to render a footer. Helpful in showing a loader while doing incremental loads |
Expand All @@ -104,11 +104,11 @@ not be as fast.
| forceNonDeterministicRendering | No | boolean | Default is false; if enabled dimensions provided in layout provider will not be strictly enforced. Use this if item dimensions cannot be accurately determined |
| extendedState | No | object | In some cases the data passed at row level may not contain all the info that the item depends upon, you can keep all other info outside and pass it down via this prop. Changing this object will cause everything to re-render. Make sure you don't change it often to ensure performance. Re-renders are heavy. |
| itemAnimator | No | ItemAnimator | Enables animating RecyclerListView item cells (shift, add, remove, etc) |
| optimizeForInsertDeleteAnimations | No | boolean | Enables you to utilize layout animations better by unmounting removed items |
| style | No | object | To pass down style to inner ScrollView |
| scrollViewProps | No | object | For all props that need to be proxied to inner/external scrollview. Put them in an object and they'll be spread and passed down. |
| layoutSize | No | Dimension | Will prevent the initial empty render required to compute the size of the listview and use these dimensions to render list items in the first render itself. This is useful for cases such as server side rendering. The prop canChangeSize has to be set to true if the size can be changed after rendering. Note that this is not the scroll view size and is used solely for layouting. |
| onItemLayout | No | number | A callback function that is executed when an item of the recyclerListView (at an index) has been layout. This can also be used as a proxy to itemsRendered kind of callbacks. |
| windowCorrectionConfig | No | object | Used to specify is window correction config and whether it should be applied to some scroll events |

For full feature set have a look at prop definitions of [RecyclerListView](https://github.com/Flipkart/recyclerlistview/blob/21049cc89ad606ec9fe8ea045dc73732ff29eac9/src/core/RecyclerListView.tsx#L540-L634)
(bottom of the file). All `ScrollView` features like `RefreshControl` also work out of the box.
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "recyclerlistview",
"version": "3.0.5",
"version": "3.2.0-beta.4",
"description": "The listview that you need and deserve. It was built for performance, uses cell recycling to achieve smooth scrolling.",
"main": "dist/reactnative/index.js",
"types": "dist/reactnative/index.d.ts",
Expand Down
59 changes: 48 additions & 11 deletions src/core/ProgressiveListView.tsx
Expand Up @@ -2,9 +2,19 @@ import RecyclerListView, { RecyclerListViewProps, RecyclerListViewState } from "
export interface ProgressiveListViewProps extends RecyclerListViewProps {
maxRenderAhead?: number;
renderAheadStep?: number;

/**
* A smaller final value can help in building up recycler pool in advance. This is only used if there is a valid updated cycle.
* e.g, if maxRenderAhead is 0 then there will be no cycle and final value will be unused
*/
finalRenderAheadOffset?: number;
}
/**
* This will incremently update renderAhread distance and render the page progressively.
* This will incrementally update renderAhead distance and render the page progressively.
* renderAheadOffset = initial value which will be incremented
* renderAheadStep = amount of increment made on each frame
* maxRenderAhead = maximum value for render ahead at the end of update cycle
* finalRenderAheadOffset = value to set after whole update cycle is completed. If undefined, final offset value will be equal to maxRenderAhead
*/
export default class ProgressiveListView extends RecyclerListView<ProgressiveListViewProps, RecyclerListViewState> {
public static defaultProps = {
Expand All @@ -13,20 +23,36 @@ export default class ProgressiveListView extends RecyclerListView<ProgressiveLis
renderAheadStep: 300,
renderAheadOffset: 0,
};
private renderAheadUdpateCallbackId?: number;
private renderAheadUpdateCallbackId?: number;
private isFirstLayoutComplete: boolean = false;

public componentDidMount(): void {
if (super.componentDidMount) {
super.componentDidMount();
super.componentDidMount();
if (!this.props.forceNonDeterministicRendering) {
this.updateRenderAheadProgressively(this.getCurrentRenderAheadOffset());
}
}

public componentWillUnmount(): void {
this.cancelRenderAheadUpdate();
super.componentWillUnmount();
}

protected onItemLayout(index: number): void {
if (!this.isFirstLayoutComplete) {
this.isFirstLayoutComplete = true;
if (this.props.forceNonDeterministicRendering) {
this.updateRenderAheadProgressively(this.getCurrentRenderAheadOffset());
}
}
this.updateRenderAheadProgessively(this.getCurrentRenderAheadOffset());
super.onItemLayout(index);
}

private updateRenderAheadProgessively(newVal: number): void {
private updateRenderAheadProgressively(newVal: number): void {
this.cancelRenderAheadUpdate(); // Cancel any pending callback.
this.renderAheadUdpateCallbackId = requestAnimationFrame(() => {
this.renderAheadUpdateCallbackId = requestAnimationFrame(() => {
if (!this.updateRenderAheadOffset(newVal)) {
this.updateRenderAheadProgessively(newVal);
this.updateRenderAheadProgressively(newVal);
} else {
this.incrementRenderAhead();
}
Expand All @@ -42,15 +68,26 @@ export default class ProgressiveListView extends RecyclerListView<ProgressiveLis
const maxContentSize = this.props.isHorizontal ? contentDimension.width : contentDimension.height;
if (currentRenderAheadOffset < maxContentSize && currentRenderAheadOffset < this.props.maxRenderAhead) {
const newRenderAheadOffset = currentRenderAheadOffset + this.props.renderAheadStep;
this.updateRenderAheadProgessively(newRenderAheadOffset);
this.updateRenderAheadProgressively(newRenderAheadOffset);
} else {
this.performFinalUpdate();
}
}
}
}

private performFinalUpdate(): void {
this.cancelRenderAheadUpdate(); // Cancel any pending callback.
this.renderAheadUpdateCallbackId = requestAnimationFrame(() => {
if (this.props.finalRenderAheadOffset !== undefined) {
this.updateRenderAheadOffset(this.props.finalRenderAheadOffset);
}
});
}

private cancelRenderAheadUpdate(): void {
if (this.renderAheadUdpateCallbackId) {
cancelAnimationFrame(this.renderAheadUdpateCallbackId);
if (this.renderAheadUpdateCallbackId !== undefined) {
cancelAnimationFrame(this.renderAheadUpdateCallbackId);
}
}
}

0 comments on commit 1a316ad

Please sign in to comment.