Skip to content

Commit

Permalink
Fixing race condition with ref setting
Browse files Browse the repository at this point in the history
  • Loading branch information
vishalnarkhede committed Jan 21, 2021
1 parent ba30c74 commit ea369e8
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 179 deletions.
38 changes: 38 additions & 0 deletions src/FlatList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { useRef, useState } from 'react';
import { FlatList, FlatListProps } from 'react-native';
import { useMvcpTuner } from './useMvcpTuner';

export type maintainVisibleContentPositionPropType = {
autoscrollToTopThreshold?: number;
minIndexForVisible: number;
};

export type FlatListComponentPropType<T = any> = FlatListProps<T> & {
maintainVisibleContentPosition: maintainVisibleContentPositionPropType;
};

export default React.forwardRef(
(props: FlatListComponentPropType, forwardedRef) => {
const flRef = useRef<FlatList>();
const [refReady, setRefReady] = useState(false);
const { extraData, maintainVisibleContentPosition: mvcp } = props;
useMvcpTuner(flRef, refReady, mvcp, extraData);

return (
<FlatList
{...props}
ref={(ref) => {
// @ts-ignore
flRef.current = ref;
setRefReady(true);

if (typeof forwardedRef === 'function') {
forwardedRef(ref);
} else if (forwardedRef?.current) {
forwardedRef.current = ref;
}
}}
/>
);
}
);
88 changes: 0 additions & 88 deletions src/Lists/FlatList.tsx

This file was deleted.

89 changes: 0 additions & 89 deletions src/Lists/ScrollView.tsx

This file was deleted.

46 changes: 46 additions & 0 deletions src/ScrollView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, { useState, useRef } from 'react';
import {
NativeModules,
FlatList,
ScrollViewProps,
ScrollView,
} from 'react-native';
import { useMvcpTuner } from './useMvcpTuner';

export const MvcpScrollViewManager = NativeModules.MvcpScrollViewManager;

export type maintainVisibleContentPositionPropType = {
autoscrollToTopThreshold?: number;
minIndexForVisible: number;
};

export type ScrollViewComponentPropType = ScrollViewProps & {
maintainVisibleContentPosition: maintainVisibleContentPositionPropType;
};

export default React.forwardRef(
(props: ScrollViewComponentPropType, forwardedRef) => {
const flRef = useRef<FlatList>();
const [refReady, setRefReady] = useState(false);

const { maintainVisibleContentPosition: mvcp } = props;
useMvcpTuner(flRef, refReady, mvcp);

return (
<ScrollView
{...props}
ref={(ref) => {
// @ts-ignore
flRef.current = ref;
setRefReady(true);

if (typeof forwardedRef === 'function') {
forwardedRef(ref);
} else if (forwardedRef?.current) {
forwardedRef.current = ref;
}
}}
/>
);
}
);
4 changes: 2 additions & 2 deletions src/index.android.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import FlatList from './Lists/FlatList';
import ScrollView from './Lists/ScrollView';
import FlatList from './FlatList';
import ScrollView from './ScrollView';

export { FlatList };
export { ScrollView };
66 changes: 66 additions & 0 deletions src/useMvcpTuner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { MutableRefObject, useEffect, useRef } from 'react';
import {
NativeModules,
FlatList,
Platform,
findNodeHandle,
} from 'react-native';

import type { maintainVisibleContentPositionPropType } from './FlatList';
export const MvcpScrollViewManager = NativeModules.MvcpScrollViewManager;

export const useMvcpTuner = (
flRef: MutableRefObject<FlatList | undefined>,
refReady: boolean,
mvcp?: maintainVisibleContentPositionPropType,
extraData?: any
) => {
const autoscrollToTopThreshold = useRef<number>();
const minIndexForVisible = useRef<number>();

useEffect(() => {
let cleanupPromise: Promise<number> | undefined;
const enableMaintainVisibleContentPosition = (): void => {
if (!mvcp || Platform.OS !== 'android' || !flRef?.current) {
return;
}

if (
autoscrollToTopThreshold.current === mvcp.autoscrollToTopThreshold &&
minIndexForVisible.current === mvcp.minIndexForVisible
) {
// Don't do anythinig if the values haven't changed
return;
}

autoscrollToTopThreshold.current =
mvcp.autoscrollToTopThreshold || -Number.MAX_SAFE_INTEGER;
minIndexForVisible.current = mvcp.minIndexForVisible || 0;

const viewTag = findNodeHandle(flRef.current);
cleanupPromise = MvcpScrollViewManager.enableMaintainVisibleContentPosition(
viewTag,
autoscrollToTopThreshold.current,
minIndexForVisible.current
);
};

enableMaintainVisibleContentPosition();

return () => {
if (
mvcp &&
autoscrollToTopThreshold.current === mvcp.autoscrollToTopThreshold &&
minIndexForVisible.current === mvcp.minIndexForVisible
) {
// Don't do anythinig if the values haven't changed
return;
}

cleanupPromise?.then((handle) => {
MvcpScrollViewManager.disableMaintainVisibleContentPosition(handle);
});
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [refReady, extraData, mvcp]);
};

0 comments on commit ea369e8

Please sign in to comment.