Skip to content

Commit

Permalink
Merge pull request #80 from Flipkart/perfAndExternalState
Browse files Browse the repository at this point in the history
Perf and external state
  • Loading branch information
muskeinsingh authored Nov 30, 2017
2 parents 396b2cf + fee3899 commit 593b763
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 36 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "recyclerlistview",
"version": "1.2.1",
"version": "1.2.2",
"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
9 changes: 8 additions & 1 deletion src/core/RecyclerListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export interface RecyclerListViewProps {
useWindowScroll?: boolean;
disableRecycling?: boolean;
forceNonDeterministicRendering?: boolean;
extendedState?: object;
}
export interface RecyclerListViewState {
renderStack: RenderStack;
Expand Down Expand Up @@ -402,7 +403,8 @@ export default class RecyclerListView extends React.Component<RecyclerListViewPr
onSizeChanged={this._onViewContainerSizeChange}
childRenderer={this.props.rowRenderer}
height={itemRect.height}
width={itemRect.width} />
width={itemRect.width}
extendedState={this.props.extendedState} />
);
}
return null;
Expand Down Expand Up @@ -537,4 +539,9 @@ RecyclerListView.propTypes = {
//Default is false, if enabled dimensions provided in layout provider will not be strictly enforced.
//Rendered dimensions will be used to relayout items. Slower if enabled.
forceNonDeterministicRendering: PropTypes.bool,

//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.
extendedState: PropTypes.object,
};
17 changes: 15 additions & 2 deletions src/core/viewrenderer/BaseViewRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,28 @@ export interface ViewRendererProps<T> {
y: number;
height: number;
width: number;
childRenderer: (type: string | number, data: T, index: number) => JSX.Element | JSX.Element[] | null;
childRenderer: (type: string | number, data: T, index: number, extendedState?: object) => JSX.Element | JSX.Element[] | null;
layoutType: string | number;
dataHasChanged: (r1: T, r2: T) => boolean;
onSizeChanged: (dim: Dimension, index: number) => void;
data: any;
index: number;
forceNonDeterministicRendering?: boolean;
isHorizontal?: boolean;
extendedState?: object;
}
export default class BaseViewRenderer<T> extends React.Component<ViewRendererProps<T>, {}> {

public shouldComponentUpdate(newProps: ViewRendererProps<any>): boolean {
return (
this.props.x !== newProps.x ||
this.props.y !== newProps.y ||
this.props.width !== newProps.width ||
this.props.height !== newProps.height ||
this.props.extendedState !== newProps.extendedState ||
(this.props.dataHasChanged && this.props.dataHasChanged(this.props.data, newProps.data))
);
}
protected renderChild(): JSX.Element | JSX.Element[] | null {
return this.props.childRenderer(this.props.layoutType, this.props.data, this.props.index, this.props.extendedState);
}
}
37 changes: 17 additions & 20 deletions src/platform/reactnative/viewrenderer/ViewRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,26 @@ import BaseViewRenderer, { ViewRendererProps } from "../../../core/viewrenderer/
* This is second of the two things recycler works on. Implemented both for web and react native.
*/
export default class ViewRenderer extends BaseViewRenderer<any> {
private _dim: Dimension = {width: 0, height: 0};
private _dim: Dimension = { width: 0, height: 0 };
private _isFirstLayoutDone: boolean = false;

constructor(props: ViewRendererProps<any>) {
super(props);
this._onLayout = this._onLayout.bind(this);
}

public shouldComponentUpdate(newProps: ViewRendererProps<any>): boolean {
return (this.props.x !== newProps.x ||
this.props.y !== newProps.y ||
this.props.width !== newProps.width ||
this.props.height !== newProps.height ||
(this.props.dataHasChanged && this.props.dataHasChanged(this.props.data, newProps.data)));
}

public render(): JSX.Element {
if (this.props.forceNonDeterministicRendering) {
return (
<View onLayout={this._onLayout}
style={{
flexDirection: this.props.isHorizontal ? "column" : "row",
left: this.props.x,
opacity: this._isFirstLayoutDone ? 1 : 0,
position: "absolute",
top: this.props.y,
}}>
{this.props.childRenderer(this.props.layoutType, this.props.data, this.props.index)}
style={{
flexDirection: this.props.isHorizontal ? "column" : "row",
left: this.props.x,
opacity: this._isFirstLayoutDone ? 1 : 0,
position: "absolute",
top: this.props.y,
}}>
{this.renderChild()}
</View>
);
} else {
Expand All @@ -48,17 +40,22 @@ export default class ViewRenderer extends BaseViewRenderer<any> {
left: 0,
position: "absolute",
top: 0,
transform: [{translateX: this.props.x}, {translateY: this.props.y}],
transform: [{ translateX: this.props.x }, { translateY: this.props.y }],
width: this.props.width,
}}>
{this.props.childRenderer(this.props.layoutType, this.props.data, this.props.index)}
{this.renderChild()}
</View>
);
}
}

private _onLayout(event: LayoutChangeEvent): void {
if (this.props.height !== event.nativeEvent.layout.height || this.props.width !== event.nativeEvent.layout.width) {
//Preventing layout thrashing in super fast scrolls where RN messes up onLayout event
const xDiff = Math.abs(this.props.x - event.nativeEvent.layout.x);
const yDiff = Math.abs(this.props.y - event.nativeEvent.layout.y);
if (xDiff < 1 && yDiff < 1 &&
(this.props.height !== event.nativeEvent.layout.height ||
this.props.width !== event.nativeEvent.layout.width)) {
this._dim.height = event.nativeEvent.layout.height;
this._dim.width = event.nativeEvent.layout.width;
if (this.props.onSizeChanged) {
Expand Down
14 changes: 2 additions & 12 deletions src/platform/web/viewrenderer/ViewRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import BaseViewRenderer, { ViewRendererProps } from "../../../core/viewrenderer/
*/
export default class ViewRenderer extends BaseViewRenderer<any> {

private _dim: Dimension = {width: 0, height: 0};
private _dim: Dimension = { width: 0, height: 0 };
private _isFirstLayoutDone: boolean = false;
private _mainDiv: HTMLDivElement | null;

Expand All @@ -23,16 +23,6 @@ export default class ViewRenderer extends BaseViewRenderer<any> {
this._checkSizeChange();
}

public shouldComponentUpdate(newProps: ViewRendererProps<any>): boolean {
return (
this.props.x !== newProps.x ||
this.props.y !== newProps.y ||
this.props.width !== newProps.width ||
this.props.height !== newProps.height ||
(this.props.dataHasChanged && this.props.dataHasChanged(this.props.data, newProps.data))
);
}

public render(): JSX.Element {
const styleObj: CSSProperties = this.props.forceNonDeterministicRendering
? {
Expand All @@ -55,7 +45,7 @@ export default class ViewRenderer extends BaseViewRenderer<any> {
};
return (
<div ref={(div) => this._mainDiv = div as HTMLDivElement | null} style={styleObj}>
{this.props.childRenderer(this.props.layoutType, this.props.data, this.props.index)}
{this.renderChild()}
</div>
);
}
Expand Down

0 comments on commit 593b763

Please sign in to comment.