Skip to content

Commit

Permalink
Compat component to maintain compatibility across react version (#387)
Browse files Browse the repository at this point in the history
* Moving logic to compat component

* Made compat component abstract

* path revert

* sticky changes

* Added will update

* Comments added

* Fixed unnecessary rerender
  • Loading branch information
naqvitalha authored and muskeinsingh committed Aug 21, 2019
1 parent f55b989 commit 1933182
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 62 deletions.
9 changes: 5 additions & 4 deletions src/core/RecyclerListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -114,7 +115,7 @@ export interface RecyclerListViewState {
internalSnapshot: Record<string, object>;
}

export default class RecyclerListView<P extends RecyclerListViewProps, S extends RecyclerListViewState> extends React.Component<P, S> {
export default class RecyclerListView<P extends RecyclerListViewProps, S extends RecyclerListViewState> extends ComponentCompat<P, S> {
public static defaultProps = {
canChangeSize: false,
disableRecycling: false,
Expand Down Expand Up @@ -166,7 +167,7 @@ export default class RecyclerListView<P extends RecyclerListViewProps, S extends
} as S;
}

public componentWillReceiveProps(newProps: RecyclerListViewProps): void {
public componentWillReceivePropsCompat(newProps: RecyclerListViewProps): void {
this._assertDependencyPresence(newProps);
this._checkAndChangeLayouts(newProps);
if (!this.props.onVisibleIndicesChanged) {
Expand Down Expand Up @@ -219,7 +220,7 @@ export default class RecyclerListView<P extends RecyclerListViewProps, S extends
}
}

public componentWillMount(): void {
public componentWillMountCompat(): void {
if (this.props.contextProvider) {
const uniqueKey = this.props.contextProvider.getUniqueKey();
if (uniqueKey) {
Expand Down Expand Up @@ -334,7 +335,7 @@ export default class RecyclerListView<P extends RecyclerListViewProps, S extends
});
}

public render(): JSX.Element {
public renderCompat(): JSX.Element {
//TODO:Talha
// const {
// layoutProvider,
Expand Down
71 changes: 36 additions & 35 deletions src/core/StickyContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@

import * as React from "react";
import * as PropTypes from "prop-types";
import {StyleProp, View, ViewStyle} from "react-native";
import RecyclerListView, {RecyclerListViewState, RecyclerListViewProps} from "./RecyclerListView";
import {ScrollEvent} from "./scrollcomponent/BaseScrollView";
import StickyObject, {StickyObjectProps, StickyObjectState} from "./sticky/StickyObject";
import { StyleProp, View, ViewStyle } from "react-native";
import RecyclerListView, { RecyclerListViewState, RecyclerListViewProps } from "./RecyclerListView";
import { ScrollEvent } from "./scrollcomponent/BaseScrollView";
import StickyObject, { StickyObjectProps } from "./sticky/StickyObject";
import StickyHeader from "./sticky/StickyHeader";
import StickyFooter from "./sticky/StickyFooter";
import CustomError from "./exceptions/CustomError";
import RecyclerListViewExceptions from "./exceptions/RecyclerListViewExceptions";
import {Layout} from "./layoutmanager/LayoutManager";
import {BaseLayoutProvider, Dimension} from "./dependencies/LayoutProvider";
import { Layout } from "./layoutmanager/LayoutManager";
import { BaseLayoutProvider, Dimension } from "./dependencies/LayoutProvider";
import { BaseDataProvider } from "./dependencies/DataProvider";
import {ReactElement} from "react";
import { ReactElement } from "react";
import { ComponentCompat } from "../utils/ComponentCompat";

export interface StickyContainerProps {
children: RecyclerChild;
Expand All @@ -28,7 +29,7 @@ export interface RecyclerChild extends React.ReactElement<RecyclerListViewProps>
ref: (recyclerRef: any) => {};
props: RecyclerListViewProps;
}
export default class StickyContainer<P extends StickyContainerProps> extends React.Component<P> {
export default class StickyContainer<P extends StickyContainerProps> extends ComponentCompat<P> {
public static propTypes = {};
private _recyclerRef: RecyclerListView<RecyclerListViewProps, RecyclerListViewState> | undefined = undefined;
private _dataProvider: BaseDataProvider;
Expand All @@ -37,8 +38,8 @@ export default class StickyContainer<P extends StickyContainerProps> extends Rea
private _rowRenderer: ((type: string | number, data: any, index: number, extendedState?: object) => JSX.Element | JSX.Element[] | null);
private _distanceFromWindow: number;

private _stickyHeaderRef: StickyHeader<StickyObjectProps, StickyObjectState> | null = null;
private _stickyFooterRef: StickyFooter<StickyObjectProps, StickyObjectState> | null = null;
private _stickyHeaderRef: StickyHeader<StickyObjectProps> | null = null;
private _stickyFooterRef: StickyFooter<StickyObjectProps> | null = null;
private _visibleIndicesAll: number[] = [];

constructor(props: P, context?: any) {
Expand All @@ -52,11 +53,11 @@ export default class StickyContainer<P extends StickyContainerProps> 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<RecyclerListViewProps> = React.cloneElement(this.props.children, {
...this.props.children.props,
Expand All @@ -65,33 +66,33 @@ export default class StickyContainer<P extends StickyContainerProps> extends Rea
onScroll: this._onScroll,
});
return (
<View style={this.props.style ? this.props.style : {flex: 1}}>
<View style={this.props.style ? this.props.style : { flex: 1 }}>
{recycler}
{this.props.stickyHeaderIndices ? (
<StickyHeader ref={(stickyHeaderRef: any) => 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 ? (
<StickyFooter ref={(stickyFooterRef: any) => 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}
</View>
);
Expand All @@ -110,15 +111,15 @@ export default class StickyContainer<P extends StickyContainerProps> extends Rea

private _getStickyHeaderRef = (stickyHeaderRef: any) => {
if (this._stickyHeaderRef !== stickyHeaderRef) {
this._stickyHeaderRef = stickyHeaderRef as (StickyHeader<StickyObjectProps, StickyObjectState> | null);
this._stickyHeaderRef = stickyHeaderRef as (StickyHeader<StickyObjectProps> | null);
// TODO: Resetting state once ref is initialized. Can look for better solution.
this._callStickyObjectsOnVisibleIndicesChanged(this._visibleIndicesAll);
}
}

private _getStickyFooterRef = (stickyFooterRef: any) => {
if (this._stickyFooterRef !== stickyFooterRef) {
this._stickyFooterRef = stickyFooterRef as (StickyFooter<StickyObjectProps, StickyObjectState> | null);
this._stickyFooterRef = stickyFooterRef as (StickyFooter<StickyObjectProps> | null);
// TODO: Resetting state once ref is initialized. Can look for better solution.
this._callStickyObjectsOnVisibleIndicesChanged(this._visibleIndicesAll);
}
Expand Down
4 changes: 2 additions & 2 deletions src/core/sticky/StickyFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<P extends StickyObjectProps, S extends StickyObjectState> extends StickyObject<P, S> {
export default class StickyFooter<P extends StickyObjectProps> extends StickyObject<P> {
constructor(props: P, context?: any) {
super(props, context);
}
Expand Down
4 changes: 2 additions & 2 deletions src/core/sticky/StickyHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<P extends StickyObjectProps, S extends StickyObjectState> extends StickyObject<P, S> {
export default class StickyHeader<P extends StickyObjectProps> extends StickyObject<P> {
constructor(props: P, context?: any) {
super(props, context);
}
Expand Down
26 changes: 11 additions & 15 deletions src/core/sticky/StickyObject.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<P extends StickyObjectProps, S extends StickyObjectState> extends React.Component<P, S> {
export default abstract class StickyObject<P extends StickyObjectProps> extends ComponentCompat<P> {
protected stickyType: StickyType = StickyType.HEADER;
protected stickyTypeMultiplier: number = 1;
protected stickyVisiblity: boolean = false;
Expand Down Expand Up @@ -63,26 +61,23 @@ export default abstract class StickyObject<P extends StickyObjectProps, S extend

constructor(props: P, context?: any) {
super(props, context);
this.state = {
visible: this.stickyVisiblity,
} as S;
}

public componentWillReceiveProps(newProps: StickyObjectProps): void {
public componentWillReceivePropsCompat(newProps: StickyObjectProps): void {
this._initParams();
this.calculateVisibleStickyIndex(newProps.stickyIndices, this._smallestVisibleIndex, this._largestVisibleIndex,
this._offsetY, newProps.getDistanceFromWindow(), this._windowBound);
this._computeLayouts(newProps.stickyIndices);
this.stickyViewVisible(this.stickyVisiblity);
this.stickyViewVisible(this.stickyVisiblity, false);
}

public render(): JSX.Element | null {
public renderCompat(): JSX.Element | null {
return (
<Animated.View style={[
{position: "absolute", width: this._scrollableWidth, transform: [{translateY: this._stickyViewOffset}]},
this.containerPosition,
]}>
{this.state.visible ?
{this.stickyVisiblity ?
this._renderSticky()
: null}
</Animated.View>
Expand Down Expand Up @@ -154,10 +149,11 @@ export default abstract class StickyObject<P extends StickyObjectProps, S extend
protected abstract getCurrentYd(currentY: number, currentHeight: number): number;
protected abstract getScrollY(offsetY: number, scrollableHeight?: number): number | undefined;

protected stickyViewVisible(_visible: boolean): void {
this.setState({
visible: _visible,
});
protected stickyViewVisible(_visible: boolean, shouldTriggerRender: boolean = true): void {
this.stickyVisiblity = _visible;
if (shouldTriggerRender) {
this.setState({});
}
}

protected boundaryProcessing(offsetY: number, distanceFromWindow: number, windowBound?: number): void {
Expand Down
5 changes: 3 additions & 2 deletions src/core/viewrenderer/BaseViewRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from "react";
import { Dimension, BaseLayoutProvider } from "../dependencies/LayoutProvider";
import ItemAnimator from "../ItemAnimator";
import { LayoutManager } from "../layoutmanager/LayoutManager";
import { ComponentCompat } from "../../utils/ComponentCompat";

/***
* View renderer is responsible for creating a container of size provided by LayoutProvider and render content inside it.
Expand All @@ -28,7 +29,7 @@ export interface ViewRendererProps<T> {
internalSnapshot?: object;
layoutProvider?: BaseLayoutProvider;
}
export default abstract class BaseViewRenderer<T> extends React.Component<ViewRendererProps<T>, {}> {
export default abstract class BaseViewRenderer<T> extends ComponentCompat<ViewRendererProps<T>, {}> {
protected animatorStyleOverrides: object | undefined;

public shouldComponentUpdate(newProps: ViewRendererProps<any>): boolean {
Expand All @@ -53,7 +54,7 @@ export default abstract class BaseViewRenderer<T> extends React.Component<ViewRe
this.animatorStyleOverrides = undefined;
this.props.itemAnimator.animateDidMount(this.props.x, this.props.y, this.getRef() as object, this.props.index);
}
public componentWillMount(): void {
public componentWillMountCompat(): void {
this.animatorStyleOverrides = this.props.itemAnimator.animateWillMount(this.props.x, this.props.y, this.props.index);
}
public componentWillUnmount(): void {
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AutoScroll } from "./utils/AutoScroll";
import { Layout, LayoutManager, Point, WrapGridLayoutManager } from "./core/layoutmanager/LayoutManager";
import ProgressiveListView from "./core/ProgressiveListView";
import { DebugHandlers } from "./core/devutils/debughandlers/DebugHandlers";
import { ComponentCompat } from "./utils/ComponentCompat";

export {
ContextProvider,
Expand All @@ -27,4 +28,5 @@ export {
OnRecreateParams,
DebugHandlers,
BaseDataProvider,
ComponentCompat,
};
2 changes: 1 addition & 1 deletion src/platform/reactnative/viewrenderer/ViewRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,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;
public render(): JSX.Element {
public renderCompat(): JSX.Element {
return this.props.forceNonDeterministicRendering ? (
<View ref={this._setRef}
onLayout={this._onLayout}
Expand Down
2 changes: 1 addition & 1 deletion src/platform/web/viewrenderer/ViewRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default class ViewRenderer extends BaseViewRenderer<any> {
this._checkSizeChange();
}

public render(): JSX.Element {
public renderCompat(): JSX.Element {
const style: CSSProperties = this.props.forceNonDeterministicRendering
? {
transform: this._getTransform(),
Expand Down
43 changes: 43 additions & 0 deletions src/utils/ComponentCompat.ts
Original file line number Diff line number Diff line change
@@ -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<T1 = {}, T2 = {}, SS = any> extends React.Component<T1, T2, SS> {
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;
}

0 comments on commit 1933182

Please sign in to comment.