Skip to content

Commit

Permalink
checking for pending onlayout in renderer (#705)
Browse files Browse the repository at this point in the history
Co-authored-by: Talha Naqvi <talha.naqvi@shopify.com>
  • Loading branch information
naqvitalha and naqvitalha authored Apr 1, 2022
1 parent 9df1a85 commit 049c297
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 13 deletions.
6 changes: 3 additions & 3 deletions src/core/RecyclerListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ export default class RecyclerListView<P extends RecyclerListViewProps, S extends
}
if (this.props.layoutProvider !== newProps.layoutProvider || this.props.isHorizontal !== newProps.isHorizontal) {
//TODO:Talha use old layout manager
this._virtualRenderer.setLayoutManager(newProps.layoutProvider.newLayoutManager(this._layout, newProps.isHorizontal));
this._virtualRenderer.setLayoutManager(newProps.layoutProvider.createLayoutManager(this._layout, newProps.isHorizontal));
if (newProps.layoutProvider.shouldRefreshWithAnchoring) {
this._virtualRenderer.refreshWithAnchor();
} else {
Expand All @@ -526,7 +526,7 @@ export default class RecyclerListView<P extends RecyclerListViewProps, S extends
const layoutManager = this._virtualRenderer.getLayoutManager();
if (layoutManager) {
const cachedLayouts = layoutManager.getLayouts();
this._virtualRenderer.setLayoutManager(newProps.layoutProvider.newLayoutManager(this._layout, newProps.isHorizontal, cachedLayouts));
this._virtualRenderer.setLayoutManager(newProps.layoutProvider.createLayoutManager(this._layout, newProps.isHorizontal, cachedLayouts));
this._refreshViewability();
}
} else if (this._relayoutReqIndex >= 0) {
Expand Down Expand Up @@ -623,7 +623,7 @@ export default class RecyclerListView<P extends RecyclerListViewProps, S extends
renderAheadOffset: props.renderAheadOffset,
};
this._virtualRenderer.setParamsAndDimensions(this._params, this._layout);
const layoutManager = props.layoutProvider.newLayoutManager(this._layout, props.isHorizontal, this._cachedLayouts);
const layoutManager = props.layoutProvider.createLayoutManager(this._layout, props.isHorizontal, this._cachedLayouts);
this._virtualRenderer.setLayoutManager(layoutManager);
this._virtualRenderer.setLayoutProvider(props.layoutProvider);
this._virtualRenderer.init();
Expand Down
29 changes: 19 additions & 10 deletions src/core/dependencies/LayoutProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,34 @@ import { Layout, WrapGridLayoutManager, LayoutManager } from "../layoutmanager/L
export abstract class BaseLayoutProvider {
//Unset if your new layout provider doesn't require firstVisibleIndex preservation on application
public shouldRefreshWithAnchoring: boolean = true;

//Return your layout manager, you get all required dependencies here. Also, make sure to use cachedLayouts. RLV might cache layouts and give back to
//in cases of conxtext preservation. Make sure you use them if provided.
public abstract newLayoutManager(renderWindowSize: Dimension, isHorizontal?: boolean, cachedLayouts?: Layout[]): LayoutManager;
private _lastLayoutManager?: LayoutManager;

//Given an index a provider is expected to return a view type which used to recycling choices
public abstract getLayoutTypeForIndex(index: number): string | number;

//Check if given dimension contradicts with your layout provider, return true for mismatches. Returning true will
//cause a relayout to fix the discrepancy
public abstract checkDimensionDiscrepancy(dimension: Dimension, type: string | number, index: number): boolean;

public createLayoutManager(renderWindowSize: Dimension, isHorizontal?: boolean, cachedLayouts?: Layout[]): LayoutManager {
this._lastLayoutManager = this.newLayoutManager(renderWindowSize, isHorizontal, cachedLayouts);
return this._lastLayoutManager;
}

public getLayoutManager(): LayoutManager | undefined {
return this._lastLayoutManager;
}

//Return your layout manager, you get all required dependencies here. Also, make sure to use cachedLayouts. RLV might cache layouts and give back to
//in cases of context preservation. Make sure you use them if provided.
// IMP: Output of this method should be cached in lastLayoutManager. It's not required to be cached, but it's good for internal optimization.
protected abstract newLayoutManager(renderWindowSize: Dimension, isHorizontal?: boolean, cachedLayouts?: Layout[]): LayoutManager;
}

export class LayoutProvider extends BaseLayoutProvider {

private _getLayoutTypeForIndex: (index: number) => string | number;
private _setLayoutForType: (type: string | number, dim: Dimension, index: number) => void;
private _tempDim: Dimension;
private _lastLayoutManager: WrapGridLayoutManager | undefined;

constructor(getLayoutTypeForIndex: (index: number) => string | number, setLayoutForType: (type: string | number, dim: Dimension, index: number) => void) {
super();
Expand All @@ -45,8 +54,7 @@ export class LayoutProvider extends BaseLayoutProvider {
}

public newLayoutManager(renderWindowSize: Dimension, isHorizontal?: boolean, cachedLayouts?: Layout[]): LayoutManager {
this._lastLayoutManager = new WrapGridLayoutManager(this, renderWindowSize, isHorizontal, cachedLayouts);
return this._lastLayoutManager;
return new WrapGridLayoutManager(this, renderWindowSize, isHorizontal, cachedLayouts);
}

//Provide a type for index, something which identifies the template of view about to load
Expand All @@ -64,8 +72,9 @@ export class LayoutProvider extends BaseLayoutProvider {
const dimension1 = dimension;
this.setComputedLayout(type, this._tempDim, index);
const dimension2 = this._tempDim;
if (this._lastLayoutManager) {
this._lastLayoutManager.setMaxBounds(dimension2);
const layoutManager = this.getLayoutManager();
if (layoutManager) {
(layoutManager as WrapGridLayoutManager).setMaxBounds(dimension2);
}
return dimension1.height !== dimension2.height || dimension1.width !== dimension2.width;
}
Expand Down
5 changes: 5 additions & 0 deletions src/core/viewrenderer/BaseViewRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface ViewRendererProps<T> {
renderItemContainer?: (props: object, parentProps: ViewRendererProps<T>, children?: React.ReactNode) => React.ReactNode;
}
export default abstract class BaseViewRenderer<T> extends ComponentCompat<ViewRendererProps<T>, {}> {
public isRendererMounted: boolean = true;
protected animatorStyleOverrides: object | undefined;

public shouldComponentUpdate(newProps: ViewRendererProps<any>): boolean {
Expand Down Expand Up @@ -59,8 +60,12 @@ export default abstract class BaseViewRenderer<T> extends ComponentCompat<ViewRe
this.animatorStyleOverrides = this.props.itemAnimator.animateWillMount(this.props.x, this.props.y, this.props.index);
}
public componentWillUnmount(): void {
this.isRendererMounted = false;
this.props.itemAnimator.animateWillUnmount(this.props.x, this.props.y, this.getRef() as object, this.props.index);
}
public componentDidUpdate(): void {
// no op
}
protected abstract getRef(): object | null;
protected renderChild(): JSX.Element | JSX.Element[] | null {
return this.props.childRenderer(this.props.layoutType, this.props.data, this.props.index, this.props.extendedState);
Expand Down
29 changes: 29 additions & 0 deletions src/platform/reactnative/viewrenderer/ViewRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from "react";
import { LayoutChangeEvent, View, ViewProperties } from "react-native";
import { Dimension } from "../../../core/dependencies/LayoutProvider";
import { LayoutManager } from "../../../core/layoutmanager/LayoutManager";
import BaseViewRenderer, { ViewRendererProps } from "../../../core/viewrenderer/BaseViewRenderer";

/***
Expand All @@ -12,6 +13,7 @@ import BaseViewRenderer, { ViewRendererProps } from "../../../core/viewrenderer/
export default class ViewRenderer extends BaseViewRenderer<any> {
private _dim: Dimension = { width: 0, height: 0 };
private _viewRef: React.Component<ViewProperties, React.ComponentState> | null = null;
private _layoutManagerRef?: LayoutManager;
public renderCompat(): JSX.Element {
const props = this.props.forceNonDeterministicRendering
? {
Expand Down Expand Up @@ -41,6 +43,26 @@ export default class ViewRenderer extends BaseViewRenderer<any> {
return this._renderItemContainer(props, this.props, this.renderChild()) as JSX.Element;
}

public componentDidUpdate(): void {
super.componentDidUpdate();
if (this.props.layoutProvider && this._layoutManagerRef) {
if (this.props.layoutProvider.getLayoutManager() !== this._layoutManagerRef) {
this._layoutManagerRef = this.props.layoutProvider.getLayoutManager();
const oldDim = {...this._dim};
setTimeout(() => {
this._forceSizeUpdate(oldDim);
}, 32);
}
}
}

public componentDidMount(): void {
super.componentDidMount();
if (this.props.layoutProvider) {
this._layoutManagerRef = this.props.layoutProvider.getLayoutManager();
}
}

protected getRef(): object | null {
return this._viewRef;
}
Expand Down Expand Up @@ -71,4 +93,11 @@ export default class ViewRenderer extends BaseViewRenderer<any> {
this.props.onItemLayout(this.props.index);
}
}
private _forceSizeUpdate = (dim: Dimension): void => {
if (dim.width === this._dim.width && dim.height === this._dim.height) {
if (this.isRendererMounted && this.props.onSizeChanged) {
this.props.onSizeChanged(this._dim, this.props.index);
}
}
}
}

0 comments on commit 049c297

Please sign in to comment.