From 1933182593e697ff1f46461250ad8ffb552b73ce Mon Sep 17 00:00:00 2001 From: Talha Naqvi Date: Wed, 21 Aug 2019 14:19:22 +0530 Subject: [PATCH] Compat component to maintain compatibility across react version (#387) * Moving logic to compat component * Made compat component abstract * path revert * sticky changes * Added will update * Comments added * Fixed unnecessary rerender --- src/core/RecyclerListView.tsx | 9 +-- src/core/StickyContainer.tsx | 71 ++++++++++--------- src/core/sticky/StickyFooter.tsx | 4 +- src/core/sticky/StickyHeader.tsx | 4 +- src/core/sticky/StickyObject.tsx | 26 +++---- src/core/viewrenderer/BaseViewRenderer.tsx | 5 +- src/index.ts | 2 + .../reactnative/viewrenderer/ViewRenderer.tsx | 2 +- .../web/viewrenderer/ViewRenderer.tsx | 2 +- src/utils/ComponentCompat.ts | 43 +++++++++++ 10 files changed, 106 insertions(+), 62 deletions(-) create mode 100644 src/utils/ComponentCompat.ts diff --git a/src/core/RecyclerListView.tsx b/src/core/RecyclerListView.tsx index 61352dd5..869230f6 100644 --- a/src/core/RecyclerListView.tsx +++ b/src/core/RecyclerListView.tsx @@ -36,6 +36,7 @@ import { TOnItemStatusChanged } from "./ViewabilityTracker"; import VirtualRenderer, { RenderStack, RenderStackItem, RenderStackParams } from "./VirtualRenderer"; import ItemAnimator, { BaseItemAnimator } from "./ItemAnimator"; import { DebugHandlers } from ".."; +import { ComponentCompat } from "../utils/ComponentCompat"; //#if [REACT-NATIVE] import ScrollComponent from "../platform/reactnative/scrollcomponent/ScrollComponent"; import ViewRenderer from "../platform/reactnative/viewrenderer/ViewRenderer"; @@ -114,7 +115,7 @@ export interface RecyclerListViewState { internalSnapshot: Record; } -export default class RecyclerListView

extends React.Component { +export default class RecyclerListView

extends ComponentCompat { public static defaultProps = { canChangeSize: false, disableRecycling: false, @@ -166,7 +167,7 @@ export default class RecyclerListView

ref: (recyclerRef: any) => {}; props: RecyclerListViewProps; } -export default class StickyContainer

extends React.Component

{ +export default class StickyContainer

extends ComponentCompat

{ public static propTypes = {}; private _recyclerRef: RecyclerListView | undefined = undefined; private _dataProvider: BaseDataProvider; @@ -37,8 +38,8 @@ export default class StickyContainer

extends Rea private _rowRenderer: ((type: string | number, data: any, index: number, extendedState?: object) => JSX.Element | JSX.Element[] | null); private _distanceFromWindow: number; - private _stickyHeaderRef: StickyHeader | null = null; - private _stickyFooterRef: StickyFooter | null = null; + private _stickyHeaderRef: StickyHeader | null = null; + private _stickyFooterRef: StickyFooter | null = null; private _visibleIndicesAll: number[] = []; constructor(props: P, context?: any) { @@ -52,11 +53,11 @@ export default class StickyContainer

extends Rea this._distanceFromWindow = childProps.distanceFromWindow ? childProps.distanceFromWindow : 0; } - public componentWillReceiveProps(newProps: P): void { + public componentWillReceivePropsCompat(newProps: P): void { this._initParams(newProps); } - public render(): JSX.Element { + public renderCompat(): JSX.Element { this._assertChildType(); const recycler: ReactElement = React.cloneElement(this.props.children, { ...this.props.children.props, @@ -65,33 +66,33 @@ export default class StickyContainer

extends Rea onScroll: this._onScroll, }); return ( - + {recycler} {this.props.stickyHeaderIndices ? ( this._getStickyHeaderRef(stickyHeaderRef)} - stickyIndices={this.props.stickyHeaderIndices} - getLayoutForIndex={this._getLayoutForIndex} - getDataForIndex={this._getDataForIndex} - getLayoutTypeForIndex={this._getLayoutTypeForIndex} - getExtendedState={this._getExtendedState} - getRLVRenderedSize={this._getRLVRenderedSize} - getContentDimension={this._getContentDimension} - getRowRenderer={this._getRowRenderer} - getDistanceFromWindow={this._getDistanceFromWindow} - overrideRowRenderer={this.props.overrideRowRenderer}/> + stickyIndices={this.props.stickyHeaderIndices} + getLayoutForIndex={this._getLayoutForIndex} + getDataForIndex={this._getDataForIndex} + getLayoutTypeForIndex={this._getLayoutTypeForIndex} + getExtendedState={this._getExtendedState} + getRLVRenderedSize={this._getRLVRenderedSize} + getContentDimension={this._getContentDimension} + getRowRenderer={this._getRowRenderer} + getDistanceFromWindow={this._getDistanceFromWindow} + overrideRowRenderer={this.props.overrideRowRenderer} /> ) : null} {this.props.stickyFooterIndices ? ( this._getStickyFooterRef(stickyFooterRef)} - stickyIndices={this.props.stickyFooterIndices} - getLayoutForIndex={this._getLayoutForIndex} - getDataForIndex={this._getDataForIndex} - getLayoutTypeForIndex={this._getLayoutTypeForIndex} - getExtendedState={this._getExtendedState} - getRLVRenderedSize={this._getRLVRenderedSize} - getContentDimension={this._getContentDimension} - getRowRenderer={this._getRowRenderer} - getDistanceFromWindow={this._getDistanceFromWindow} - overrideRowRenderer={this.props.overrideRowRenderer}/> + stickyIndices={this.props.stickyFooterIndices} + getLayoutForIndex={this._getLayoutForIndex} + getDataForIndex={this._getDataForIndex} + getLayoutTypeForIndex={this._getLayoutTypeForIndex} + getExtendedState={this._getExtendedState} + getRLVRenderedSize={this._getRLVRenderedSize} + getContentDimension={this._getContentDimension} + getRowRenderer={this._getRowRenderer} + getDistanceFromWindow={this._getDistanceFromWindow} + overrideRowRenderer={this.props.overrideRowRenderer} /> ) : null} ); @@ -110,7 +111,7 @@ export default class StickyContainer

extends Rea private _getStickyHeaderRef = (stickyHeaderRef: any) => { if (this._stickyHeaderRef !== stickyHeaderRef) { - this._stickyHeaderRef = stickyHeaderRef as (StickyHeader | null); + this._stickyHeaderRef = stickyHeaderRef as (StickyHeader | null); // TODO: Resetting state once ref is initialized. Can look for better solution. this._callStickyObjectsOnVisibleIndicesChanged(this._visibleIndicesAll); } @@ -118,7 +119,7 @@ export default class StickyContainer

extends Rea private _getStickyFooterRef = (stickyFooterRef: any) => { if (this._stickyFooterRef !== stickyFooterRef) { - this._stickyFooterRef = stickyFooterRef as (StickyFooter | null); + this._stickyFooterRef = stickyFooterRef as (StickyFooter | null); // TODO: Resetting state once ref is initialized. Can look for better solution. this._callStickyObjectsOnVisibleIndicesChanged(this._visibleIndicesAll); } diff --git a/src/core/sticky/StickyFooter.tsx b/src/core/sticky/StickyFooter.tsx index 2c9e512d..172fbfaf 100644 --- a/src/core/sticky/StickyFooter.tsx +++ b/src/core/sticky/StickyFooter.tsx @@ -2,10 +2,10 @@ * Created by ananya.chandra on 20/09/18. */ -import StickyObject, {StickyObjectProps, StickyObjectState, StickyType} from "./StickyObject"; +import StickyObject, {StickyObjectProps, StickyType} from "./StickyObject"; import BinarySearch, {ValueAndIndex} from "../../utils/BinarySearch"; -export default class StickyFooter

extends StickyObject { +export default class StickyFooter

extends StickyObject

{ constructor(props: P, context?: any) { super(props, context); } diff --git a/src/core/sticky/StickyHeader.tsx b/src/core/sticky/StickyHeader.tsx index 22db8a28..a2185b8a 100644 --- a/src/core/sticky/StickyHeader.tsx +++ b/src/core/sticky/StickyHeader.tsx @@ -2,10 +2,10 @@ * Created by ananya.chandra on 20/09/18. */ -import StickyObject, {StickyObjectProps, StickyObjectState, StickyType} from "./StickyObject"; +import StickyObject, {StickyObjectProps, StickyType} from "./StickyObject"; import BinarySearch, {ValueAndIndex} from "../../utils/BinarySearch"; -export default class StickyHeader

extends StickyObject { +export default class StickyHeader

extends StickyObject

{ constructor(props: P, context?: any) { super(props, context); } diff --git a/src/core/sticky/StickyObject.tsx b/src/core/sticky/StickyObject.tsx index 16b34685..38c401ce 100644 --- a/src/core/sticky/StickyObject.tsx +++ b/src/core/sticky/StickyObject.tsx @@ -8,6 +8,7 @@ import {Layout} from "../layoutmanager/LayoutManager"; import {Dimension} from "../dependencies/LayoutProvider"; import RecyclerListViewExceptions from "../exceptions/RecyclerListViewExceptions"; import CustomError from "../exceptions/CustomError"; +import { ComponentCompat } from "../../utils/ComponentCompat"; export enum StickyType { HEADER, @@ -25,10 +26,7 @@ export interface StickyObjectProps { getDistanceFromWindow: () => number; overrideRowRenderer?: (type: string | number | undefined, data: any, index: number, extendedState?: object) => JSX.Element | JSX.Element[] | null; } -export interface StickyObjectState { - visible: boolean; -} -export default abstract class StickyObject

extends React.Component { +export default abstract class StickyObject

extends ComponentCompat

{ protected stickyType: StickyType = StickyType.HEADER; protected stickyTypeMultiplier: number = 1; protected stickyVisiblity: boolean = false; @@ -63,26 +61,23 @@ export default abstract class StickyObject

- {this.state.visible ? + {this.stickyVisiblity ? this._renderSticky() : null} @@ -154,10 +149,11 @@ export default abstract class StickyObject

{ internalSnapshot?: object; layoutProvider?: BaseLayoutProvider; } -export default abstract class BaseViewRenderer extends React.Component, {}> { +export default abstract class BaseViewRenderer extends ComponentCompat, {}> { protected animatorStyleOverrides: object | undefined; public shouldComponentUpdate(newProps: ViewRendererProps): boolean { @@ -53,7 +54,7 @@ export default abstract class BaseViewRenderer extends React.Component { private _dim: Dimension = { width: 0, height: 0 }; private _viewRef: React.Component | null = null; - public render(): JSX.Element { + public renderCompat(): JSX.Element { return this.props.forceNonDeterministicRendering ? ( { this._checkSizeChange(); } - public render(): JSX.Element { + public renderCompat(): JSX.Element { const style: CSSProperties = this.props.forceNonDeterministicRendering ? { transform: this._getTransform(), diff --git a/src/utils/ComponentCompat.ts b/src/utils/ComponentCompat.ts new file mode 100644 index 00000000..c9bfec31 --- /dev/null +++ b/src/utils/ComponentCompat.ts @@ -0,0 +1,43 @@ +import * as React from "react"; + +//Interim solve given we want to be active on old react as well for now. +export abstract class ComponentCompat extends React.Component { + private _hasRenderedOnce: boolean = false; + private _didPropsChange: boolean = false; + + constructor(props: T1, context?: any) { + super(props, context); + } + + public shouldComponentUpdate(newProps: T1, newState: T2): boolean { + if (this.props !== newProps) { + this.componentWillReceivePropsCompat(newProps); + } + return true; + } + + //setState inside will not update the existing cycle, not a true replacement for componentWillReceiveProps + public componentWillReceivePropsCompat(newProps: T1): void { + //no op + } + + public componentWillMountCompat(): void { + //no op + } + + public componentWillUpdateCompat(): void { + //no op + } + + public render(): React.ReactNode { + if (!this._hasRenderedOnce) { + this._hasRenderedOnce = true; + this.componentWillMountCompat(); + } else { + this.componentWillUpdateCompat(); + } + return this.renderCompat(); + } + + public abstract renderCompat(): React.ReactNode; +}