From 17cbe355d427ad08bf6a3f7293432c7b0b365e2c Mon Sep 17 00:00:00 2001 From: "Luciano J. Bracco" Date: Thu, 20 Apr 2023 12:15:44 -0300 Subject: [PATCH 01/10] feat: shopify/flash-list support --- README.md | 5 ++ example/index.js | 6 +-- package.json | 4 +- src/FlashList.tsx | 119 ++++++++++++++++++++++++++++++++++++++++++++++ src/helpers.tsx | 3 ++ src/index.tsx | 2 + yarn.lock | 40 +++++++++++++++- 7 files changed, 173 insertions(+), 6 deletions(-) create mode 100644 src/FlashList.tsx diff --git a/README.md b/README.md index 8f47b56c..dc5cbf22 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ - [Tabs.Container](#tabscontainer) - [Tabs.Lazy](#tabslazy) - [Tabs.FlatList](#tabsflatlist) + - [Tabs.FlashList](#tabsflatlist) - [Tabs.SectionList](#tabssectionlist) - [Tabs.ScrollView](#tabsscrollview) - [Ref](#ref) @@ -237,6 +238,10 @@ Typically used internally, but if you want to mix lazy and regular screens you c Use like a regular FlatList. +### Tabs.FlashList + +Use like a regular FlashList. + ### Tabs.ScrollView Use like a regular ScrollView. diff --git a/example/index.js b/example/index.js index 1d6e981e..a8644b15 100644 --- a/example/index.js +++ b/example/index.js @@ -1,8 +1,8 @@ -import { registerRootComponent } from 'expo'; +import { registerRootComponent } from 'expo' -import App from './App'; +import App from './App' // registerRootComponent calls AppRegistry.registerComponent('main', () => App); // It also ensures that whether you load the app in Expo Go or in a native build, // the environment is set up appropriately -registerRootComponent(App); +registerRootComponent(App) diff --git a/package.json b/package.json index 5f5ef509..fc7dae2a 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "@commitlint/config-conventional": "^9.1.1", "@microsoft/tsdoc": "^0.13.0", "@release-it/conventional-changelog": "^1.1.4", + "@shopify/flash-list": "^1.4.2", "@types/react": "^16.9.44", "@types/react-native": "0.63.4", "babel-jest": "^26.2.2", @@ -77,7 +78,8 @@ "react": "*", "react-native": "*", "react-native-pager-view": "*", - "react-native-reanimated": ">=2.15.0" + "react-native-reanimated": ">=2.15.0", + "@shopify/flash-list": ">=1.0.0" }, "husky": { "hooks": { diff --git a/src/FlashList.tsx b/src/FlashList.tsx new file mode 100644 index 00000000..4ca39a15 --- /dev/null +++ b/src/FlashList.tsx @@ -0,0 +1,119 @@ +import { FlashList as SPFlashList, FlashListProps } from '@shopify/flash-list' +import React from 'react' + +import { AnimatedFlashList } from './helpers' +import { + useAfterMountEffect, + useChainCallback, + useCollapsibleStyle, + useConvertAnimatedToValue, + useScrollHandlerY, + useSharedAnimatedRef, + useTabNameContext, + useTabsContext, + useUpdateScrollViewContentSize, +} from './hooks' + +/** + * Used as a memo to prevent rerendering too often when the context changes. + * See: https://github.com/facebook/react/issues/15156#issuecomment-474590693 + */ + +type FlashListMemoProps = React.PropsWithChildren> +type FlashListMemoRef = SPFlashList + +const FlashListMemo = React.memo( + React.forwardRef((props, passRef) => { + return + }) +) + +const EMPTY_HANDLER = () => { + // +} + +function FlashListImpl( + { + style, + onContentSizeChange, + refreshControl, + ...rest + }: Omit, 'onScroll'>, + passRef: React.Ref> +) { + const name = useTabNameContext() + const { setRef, contentInset } = useTabsContext() + const ref = useSharedAnimatedRef(passRef) + + const { scrollHandler, enable } = useScrollHandlerY(name) + + useAfterMountEffect(() => { + // we enable the scroll event after mounting + // otherwise we get an `onScroll` call with the initial scroll position which can break things + enable(true) + }) + + const { progressViewOffset } = useCollapsibleStyle() + + React.useEffect(() => { + setRef(name, ref) + }, [name, ref, setRef]) + + const scrollContentSizeChange = useUpdateScrollViewContentSize({ + name, + }) + + const scrollContentSizeChangeHandlers = useChainCallback( + React.useMemo(() => [scrollContentSizeChange, onContentSizeChange], [ + onContentSizeChange, + scrollContentSizeChange, + ]) + ) + + const memoRefreshControl = React.useMemo( + () => + refreshControl && + React.cloneElement(refreshControl, { + progressViewOffset, + ...refreshControl.props, + }), + [progressViewOffset, refreshControl] + ) + + const contentInsetValue = useConvertAnimatedToValue(contentInset) + + const memoContentInset = React.useMemo(() => ({ top: contentInsetValue }), [ + contentInsetValue, + ]) + + const memoContentOffset = React.useMemo( + () => ({ x: 0, y: -contentInsetValue }), + [contentInsetValue] + ) + + return ( + // @ts-expect-error typescript complains about `unknown` in the memo, it should be T + + ) +} + +/** + * Use like a regular FlashList. + */ +export const FlashList = React.forwardRef(FlashListImpl) as ( + p: FlashListProps & { ref?: React.Ref> } +) => React.ReactElement diff --git a/src/helpers.tsx b/src/helpers.tsx index 4a6b572f..7a1cde49 100644 --- a/src/helpers.tsx +++ b/src/helpers.tsx @@ -1,3 +1,4 @@ +import { FlashList } from '@shopify/flash-list' import { FlatList, Platform, SectionList, I18nManager } from 'react-native' import Animated, { scrollTo } from 'react-native-reanimated' @@ -28,3 +29,5 @@ export function scrollToImpl( scrollTo(ref, x, y, animated) } + +export const AnimatedFlashList = Animated.createAnimatedComponent(FlashList) diff --git a/src/index.tsx b/src/index.tsx index 1ff61cde..3655e470 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,5 @@ import { Container } from './Container' +import { FlashList } from './FlashList' import { FlatList } from './FlatList' import { Lazy } from './Lazy' import { MaterialTabBarProps, MaterialTabItemProps } from './MaterialTabBar' @@ -36,6 +37,7 @@ export const Tabs = { FlatList, ScrollView, SectionList, + FlashList, } export { Container, Tab, Lazy, FlatList, ScrollView, SectionList } diff --git a/yarn.lock b/yarn.lock index 56e531d4..2620d98a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2478,6 +2478,14 @@ prepend-file "^1.3.1" release-it "^13.5.6" +"@shopify/flash-list@^1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@shopify/flash-list/-/flash-list-1.4.2.tgz#86c188f42fcb4bb2a569c0354978e9148a83d1f3" + integrity sha512-MX3vyiHdyCoveqrv+0LufQVlLpoWMZ/bpn+4v6RKfW6ZE0+z8S7WdZTU5Gdj7IFPlkulJAtdFn4Jl0V7tDvd6A== + dependencies: + recyclerlistview "4.2.0" + tslib "2.4.0" + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -7884,7 +7892,7 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= -lodash.debounce@^4.0.8: +lodash.debounce@4.0.8, lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= @@ -9397,6 +9405,15 @@ prompts@^2.4.0: kleur "^3.0.3" sisteransi "^1.0.5" +prop-types@15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" @@ -9521,7 +9538,7 @@ react-docgen-typescript@^1.20.5: resolved "https://registry.yarnpkg.com/react-docgen-typescript/-/react-docgen-typescript-1.20.5.tgz#fb8d78a707243498436c2952bd3f6f488a68d4f3" integrity sha512-AbLGMtn76bn7SYBJSSaKJrZ0lgNRRR3qL60PucM5M4v/AXyC8221cKBXW5Pyt9TfDRfe+LDnPNlg7TibxX0ovA== -react-is@^16.12.0, react-is@^16.8.1, react-is@^16.8.4: +react-is@^16.12.0, react-is@^16.13.1, react-is@^16.8.1, react-is@^16.8.4: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -9749,6 +9766,15 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" +recyclerlistview@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/recyclerlistview/-/recyclerlistview-4.2.0.tgz#a140149aaa470c9787a1426452651934240d69ef" + integrity sha512-uuBCi0c+ggqHKwrzPX4Z/mJOzsBbjZEAwGGmlwpD/sD7raXixdAbdJ6BTcAmuWG50Cg4ru9p12M94Njwhr/27A== + dependencies: + lodash.debounce "4.0.8" + prop-types "15.8.1" + ts-object-utils "0.0.5" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -11127,6 +11153,11 @@ ts-node@^9.1.1: source-map-support "^0.5.17" yn "3.1.1" +ts-object-utils@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/ts-object-utils/-/ts-object-utils-0.0.5.tgz#95361cdecd7e52167cfc5e634c76345e90a26077" + integrity sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA== + tsconfig-paths@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" @@ -11137,6 +11168,11 @@ tsconfig-paths@^3.9.0: minimist "^1.2.0" strip-bom "^3.0.0" +tslib@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" From 46187ca4891ff90249d5e72d200deb4ab0922b3e Mon Sep 17 00:00:00 2001 From: "Luciano J. Bracco" Date: Thu, 20 Apr 2023 14:18:55 -0300 Subject: [PATCH 02/10] feat: added example for FlashList --- example/src/AnimatedHeader.tsx | 2 +- example/src/FlashList.tsx | 17 ++ example/src/Shared/ContactsFlashList.tsx | 193 ++++++++++++++++++ .../src/Shared/ExampleComponentFlashList.tsx | 42 ++++ src/index.tsx | 2 +- 5 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 example/src/FlashList.tsx create mode 100644 example/src/Shared/ContactsFlashList.tsx create mode 100644 example/src/Shared/ExampleComponentFlashList.tsx diff --git a/example/src/AnimatedHeader.tsx b/example/src/AnimatedHeader.tsx index a16a9d58..49d40f8f 100644 --- a/example/src/AnimatedHeader.tsx +++ b/example/src/AnimatedHeader.tsx @@ -8,7 +8,7 @@ import Animated, { } from 'react-native-reanimated' import { useCurrentTabScrollY } from '../../src/hooks' -import ExampleComponent from './Shared/ExampleComponent' +import ExampleComponent from './Shared/ExampleComponentFlashList' import ReText from './Shared/ReText' import { ExampleComponentType } from './types' diff --git a/example/src/FlashList.tsx b/example/src/FlashList.tsx new file mode 100644 index 00000000..033de12b --- /dev/null +++ b/example/src/FlashList.tsx @@ -0,0 +1,17 @@ +import React from 'react' + +import ExampleComponent from './Shared/ExampleComponent' +import { buildHeader } from './Shared/Header' +import { ExampleComponentType } from './types' + +const title = 'Lazy Example' + +const Header = buildHeader(title) + +const DefaultExample: ExampleComponentType = () => { + return +} + +DefaultExample.title = title + +export default DefaultExample diff --git a/example/src/Shared/ContactsFlashList.tsx b/example/src/Shared/ContactsFlashList.tsx new file mode 100644 index 00000000..9379738f --- /dev/null +++ b/example/src/Shared/ContactsFlashList.tsx @@ -0,0 +1,193 @@ +import * as React from 'react' +import { + View, + Text, + StyleSheet, + Platform, + TouchableOpacity, +} from 'react-native' +import * as Tabs from 'react-native-collapsible-tab-view' +import Animated, { + interpolate, + useAnimatedStyle, + useDerivedValue, +} from 'react-native-reanimated' + +import { useRefresh } from './useRefresh' + +type Item = { name: string; number: number } + +const CONTACTS: Item[] = [ + { name: 'Marissa Castillo', number: 7766398169 }, + { name: 'Denzel Curry', number: 9394378449 }, + { name: 'Miles Ferguson', number: 8966872888 }, + { name: 'Desiree Webster', number: 6818656371 }, + { name: 'Samantha Young', number: 6538288534 }, + { name: 'Irene Hunter', number: 2932176249 }, + { name: 'Annie Ryan', number: 4718456627 }, + { name: 'Sasha Oliver', number: 9743195919 }, + { name: 'Jarrod Avila', number: 8339212305 }, + { name: 'Griffin Weaver', number: 6059349721 }, + { name: 'Emilee Moss', number: 7382905180 }, + { name: 'Angelique Oliver', number: 9689298436 }, + { name: 'Emanuel Little', number: 6673376805 }, + { name: 'Wayne Day', number: 6918839582 }, + { name: 'Lauren Reese', number: 4652613201 }, + { name: 'Kailey Ward', number: 2232609512 }, + { name: 'Gabrielle Newman', number: 2837997127 }, + { name: 'Luke Strickland', number: 8404732322 }, + { name: 'Payton Garza', number: 7916140875 }, + { name: 'Anna Moss', number: 3504954657 }, + { name: 'Kailey Vazquez', number: 3002136330 }, + { name: 'Jennifer Coleman', number: 5469629753 }, + { name: 'Cindy Casey', number: 8446175026 }, + { name: 'Dillon Doyle', number: 5614510703 }, + { name: 'Savannah Garcia', number: 5634775094 }, + { name: 'Kailey Hudson', number: 3289239675 }, + { name: 'Ariel Green', number: 2103492196 }, + { name: 'Weston Perez', number: 2984221823 }, + { name: 'Kari Juarez', number: 9502125065 }, + { name: 'Sara Sanders', number: 7696668206 }, + { name: 'Griffin Le', number: 3396937040 }, + { name: 'Fernando Valdez', number: 9124257306 }, + { name: 'Taylor Marshall', number: 9656072372 }, + { name: 'Elias Dunn', number: 9738536473 }, + { name: 'Diane Barrett', number: 6886824829 }, + { name: 'Samuel Freeman', number: 5523948094 }, + { name: 'Irene Garza', number: 2077694008 }, + { name: 'Devante Alvarez', number: 9897002645 }, + { name: 'Sydney Floyd', number: 6462897254 }, + { name: 'Toni Dixon', number: 3775448213 }, + { name: 'Anastasia Spencer', number: 4548212752 }, + { name: 'Reid Cortez', number: 6668056507 }, + { name: 'Ramon Duncan', number: 8889157751 }, + { name: 'Kenny Moreno', number: 5748219540 }, + { name: 'Shelby Craig', number: 9473708675 }, + { name: 'Jordyn Brewer', number: 7552277991 }, + { name: 'Tanya Walker', number: 4308189657 }, + { name: 'Nolan Figueroa', number: 9173443776 }, + { name: 'Sophia Gibbs', number: 6435942770 }, + { name: 'Vincent Sandoval', number: 2606111495 }, +] + +class ContactItem extends React.PureComponent<{ + item: { name: string; number: number } +}> { + render() { + const { item } = this.props + + return ( + + + + + {item.name.slice(0, 1).toUpperCase()} + + + + {item.name} + {item.number} + + + + ) + } +} + +const ItemSeparator = () => + +const renderItem = ({ item }: { item: Item }) => + +const ListEmptyComponent = () => { + const { top, height } = Tabs.useHeaderMeasurements() + const translateY = useDerivedValue(() => { + return interpolate( + -top.value, + [0, height.value || 0], + [-(height.value || 0) / 2, 0] + ) + }, [height]) + + const stylez = useAnimatedStyle(() => { + return { + transform: [ + { + translateY: translateY.value, + }, + ], + } + }) + + return ( + + Centered Empty List! + + ) +} + +const Contacts: React.FC<{ + emptyContacts?: boolean + nestedScrollEnabled?: boolean +}> = ({ emptyContacts, nestedScrollEnabled }) => { + const [isRefreshing, startRefreshing] = useRefresh() + + return ( + String(i)} + renderItem={renderItem} + ItemSeparatorComponent={ItemSeparator} + ListEmptyComponent={ListEmptyComponent} + // see https://github.com/software-mansion/react-native-reanimated/issues/1703 + onRefresh={Platform.OS === 'ios' ? startRefreshing : undefined} + refreshing={Platform.OS === 'ios' ? isRefreshing : undefined} + nestedScrollEnabled={nestedScrollEnabled} + /> + ) +} + +export default Contacts + +const styles = StyleSheet.create({ + item: { + backgroundColor: 'white', + flexDirection: 'row', + alignItems: 'center', + padding: 8, + }, + avatar: { + height: 36, + width: 36, + borderRadius: 18, + backgroundColor: '#e91e63', + alignItems: 'center', + justifyContent: 'center', + }, + letter: { + color: 'white', + fontWeight: 'bold', + }, + details: { + margin: 8, + }, + name: { + fontWeight: 'bold', + fontSize: 14, + color: 'black', + }, + number: { + fontSize: 12, + color: '#999', + }, + separator: { + height: StyleSheet.hairlineWidth, + backgroundColor: 'rgba(0, 0, 0, .08)', + }, + listEmpty: { + alignItems: 'center', + flex: 1, + justifyContent: 'center', + borderColor: 'black', + borderWidth: 10, + }, +}) diff --git a/example/src/Shared/ExampleComponentFlashList.tsx b/example/src/Shared/ExampleComponentFlashList.tsx new file mode 100644 index 00000000..43cd4bee --- /dev/null +++ b/example/src/Shared/ExampleComponentFlashList.tsx @@ -0,0 +1,42 @@ +import React from 'react' +import { + Tabs, + CollapsibleRef, + CollapsibleProps, +} from 'react-native-collapsible-tab-view' + +import Albums from './Albums' +import Article from './Article' +import Contacts from './ContactsFlashList' +import { HEADER_HEIGHT } from './Header' +import SectionContacts from './SectionContacts' + +type Props = { + emptyContacts?: boolean + hideArticleTab?: boolean +} & Partial + +const Example = React.forwardRef( + ({ emptyContacts, ...props }, ref) => { + return ( + + {props.hideArticleTab ? ( + +
+ + ) : null} + + + + + + + + + + + ) + } +) + +export default Example diff --git a/src/index.tsx b/src/index.tsx index 3655e470..019eb4bc 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -40,7 +40,7 @@ export const Tabs = { FlashList, } -export { Container, Tab, Lazy, FlatList, ScrollView, SectionList } +export { Container, Tab, Lazy, FlatList, ScrollView, SectionList, FlashList } export { useCurrentTabScrollY, useHeaderMeasurements, From 152e03e8dea45398f3b0f78f0bfa0857346f36db Mon Sep 17 00:00:00 2001 From: "Luciano J. Bracco" Date: Thu, 20 Apr 2023 15:29:10 -0300 Subject: [PATCH 03/10] feat: fixed issue realted to scrollTo in Reanimated --- example/ios/Podfile.lock | 14 +++++--- .../xcshareddata/IDEWorkspaceChecks.plist | 8 +++++ example/package.json | 1 + example/yarn.lock | 33 +++++++++++++++++-- src/FlashList.tsx | 20 ++++++----- 5 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 example/ios/ReactNativeCollapsibleTabViewDemos.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index ea85da22..a5b63460 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -335,6 +335,8 @@ PODS: - React-jsi (= 0.71.6) - React-logger (= 0.71.6) - React-perflogger (= 0.71.6) + - RNFlashList (1.4.2): + - React-Core - RNGestureHandler (2.9.0): - React-Core - RNReanimated (2.14.4): @@ -409,6 +411,7 @@ DEPENDENCIES: - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - "RNFlashList (from `../node_modules/@shopify/flash-list`)" - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNReanimated (from `../node_modules/react-native-reanimated`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) @@ -500,6 +503,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" ReactCommon: :path: "../node_modules/react-native/ReactCommon" + RNFlashList: + :path: "../node_modules/@shopify/flash-list" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNReanimated: @@ -508,8 +513,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: - boost: 57d2868c099736d80fcd648bf211b4431e51a558 - DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 + boost: a7c83b31436843459a1961bfd74b96033dc77234 + DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662 EXApplication: d8f53a7eee90a870a75656280e8d4b85726ea903 EXConstants: f348da07e21b23d2b085e270d7b74f282df1a7d9 EXFileSystem: 844e86ca9b5375486ecc4ef06d3838d5597d895d @@ -521,7 +526,7 @@ SPEC CHECKSUMS: FBLazyVector: a83ceaa8a8581003a623facdb3c44f6d4f342ac5 FBReactNativeSpec: 85eee79837cb797ab6176f0243a2b40511c09158 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 - glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b + glog: 476ee3e89abb49e07f822b48323c51c57124b572 RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 RCTRequired: 5c6fd63b03abb06947d348dadac51c93e3485bd8 RCTTypeSafety: 1c66daedd66f674e39ce9f40782f0d490c78b175 @@ -550,10 +555,11 @@ SPEC CHECKSUMS: React-RCTVibration: 73d201599a64ea14b4e0b8f91b64970979fd92e6 React-runtimeexecutor: 8692ac548bec648fa121980ccb4304afd136d584 ReactCommon: e1067159764444e5db7c14e294d5cd79fb159c59 + RNFlashList: 7fbca4fc075484a9426f1610d648dbea2de94eb0 RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39 RNReanimated: cc5e3aa479cb9170bcccf8204291a6950a3be128 Yoga: ba09b6b11e6139e3df8229238aa794205ca6a02a PODFILE CHECKSUM: 4a1d4df08804ad081db13b6bea02bebbb098497a -COCOAPODS: 1.12.1 +COCOAPODS: 1.11.3 diff --git a/example/ios/ReactNativeCollapsibleTabViewDemos.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/ReactNativeCollapsibleTabViewDemos.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/example/ios/ReactNativeCollapsibleTabViewDemos.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/package.json b/example/package.json index da3fce33..b5ab85c9 100644 --- a/example/package.json +++ b/example/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@expo/vector-icons": "^13.0.0", + "@shopify/flash-list": "^1.4.2", "expo": "^48.0.0", "expo-constants": "~14.2.1", "expo-splash-screen": "~0.18.1", diff --git a/example/yarn.lock b/example/yarn.lock index 68f40f31..0388bebe 100644 --- a/example/yarn.lock +++ b/example/yarn.lock @@ -1761,6 +1761,14 @@ component-type "^1.2.1" join-component "^1.1.0" +"@shopify/flash-list@^1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@shopify/flash-list/-/flash-list-1.4.2.tgz#86c188f42fcb4bb2a569c0354978e9148a83d1f3" + integrity sha512-MX3vyiHdyCoveqrv+0LufQVlLpoWMZ/bpn+4v6RKfW6ZE0+z8S7WdZTU5Gdj7IFPlkulJAtdFn4Jl0V7tDvd6A== + dependencies: + recyclerlistview "4.2.0" + tslib "2.4.0" + "@sideway/address@^4.1.3": version "4.1.4" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" @@ -4330,7 +4338,7 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash.debounce@^4.0.8: +lodash.debounce@4.0.8, lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== @@ -5433,7 +5441,7 @@ prompts@^2.3.2, prompts@^2.4.0: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@*, prop-types@^15.7.2: +prop-types@*, prop-types@15.8.1, prop-types@^15.7.2: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -5682,6 +5690,15 @@ recast@^0.20.4: source-map "~0.6.1" tslib "^2.0.1" +recyclerlistview@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/recyclerlistview/-/recyclerlistview-4.2.0.tgz#a140149aaa470c9787a1426452651934240d69ef" + integrity sha512-uuBCi0c+ggqHKwrzPX4Z/mJOzsBbjZEAwGGmlwpD/sD7raXixdAbdJ6BTcAmuWG50Cg4ru9p12M94Njwhr/27A== + dependencies: + lodash.debounce "4.0.8" + prop-types "15.8.1" + ts-object-utils "0.0.5" + regenerate-unicode-properties@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" @@ -6543,7 +6560,17 @@ ts-interface-checker@^0.1.9: resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== -tslib@^2.0.1, tslib@^2.1.0, tslib@^2.4.0: +ts-object-utils@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/ts-object-utils/-/ts-object-utils-0.0.5.tgz#95361cdecd7e52167cfc5e634c76345e90a26077" + integrity sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA== + +tslib@2.4.0, tslib@^2.0.1, tslib@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + +tslib@^2.4.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== diff --git a/src/FlashList.tsx b/src/FlashList.tsx index 4ca39a15..88cf9b00 100644 --- a/src/FlashList.tsx +++ b/src/FlashList.tsx @@ -28,10 +28,6 @@ const FlashListMemo = React.memo( }) ) -const EMPTY_HANDLER = () => { - // -} - function FlashListImpl( { style, @@ -95,18 +91,24 @@ function FlashListImpl( // @ts-expect-error typescript complains about `unknown` in the memo, it should be T { + // https://github.com/Shopify/flash-list/blob/2d31530ed447a314ec5429754c7ce88dad8fd087/src/FlashList.tsx#L829 + // We are not accessing the right element or view of the Flashlist (recyclerlistview). So we need to give + // this ref the access to it + // eslint-ignore + ;(ref as any)(value?.recyclerlistview_unsafe) + }} bouncesZoom={false} - progressViewOffset={progressViewOffset} onScroll={scrollHandler} - onContentSizeChange={scrollContentSizeChangeHandlers} scrollEventThrottle={16} contentInset={memoContentInset} contentOffset={memoContentOffset} - automaticallyAdjustContentInsets={false} refreshControl={memoRefreshControl} // workaround for: https://github.com/software-mansion/react-native-reanimated/issues/2735 - onMomentumScrollEnd={EMPTY_HANDLER} + onMomentumScrollEnd={() => {}} + progressViewOffset={progressViewOffset} + automaticallyAdjustContentInsets={false} + onContentSizeChange={scrollContentSizeChangeHandlers} /> ) } From 87a0f52f1fde80a2a9d853ebe1445ad4ea02a88b Mon Sep 17 00:00:00 2001 From: "Luciano J. Bracco" Date: Thu, 20 Apr 2023 15:33:08 -0300 Subject: [PATCH 04/10] chore: created example to showcase flashlist --- example/src/AnimatedHeader.tsx | 2 +- example/src/App.tsx | 2 + example/src/FlashList.tsx | 82 ++++++++++++++++++++++++++++++---- 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/example/src/AnimatedHeader.tsx b/example/src/AnimatedHeader.tsx index 49d40f8f..a16a9d58 100644 --- a/example/src/AnimatedHeader.tsx +++ b/example/src/AnimatedHeader.tsx @@ -8,7 +8,7 @@ import Animated, { } from 'react-native-reanimated' import { useCurrentTabScrollY } from '../../src/hooks' -import ExampleComponent from './Shared/ExampleComponentFlashList' +import ExampleComponent from './Shared/ExampleComponent' import ReText from './Shared/ReText' import { ExampleComponentType } from './types' diff --git a/example/src/App.tsx b/example/src/App.tsx index 30cedffc..c2f3d007 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -19,6 +19,7 @@ import ConditionalTabs from './ConditionalTabs' import Default from './Default' import DefaultCustomLabels from './DefaultCustomLabels' import DynamicTabs from './DynamicTabs' +import FlashList from './FlashList' import HeaderOverscrollExample from './HeaderOverscroll' import Lazy from './Lazy' import MinHeaderHeight from './MinHeaderHeight' @@ -36,6 +37,7 @@ import { ExampleComponentType } from './types' const EXAMPLE_COMPONENTS: ExampleComponentType[] = [ Default, + FlashList, DefaultCustomLabels, Snap, RevealHeaderOnScroll, diff --git a/example/src/FlashList.tsx b/example/src/FlashList.tsx index 033de12b..5d610fb2 100644 --- a/example/src/FlashList.tsx +++ b/example/src/FlashList.tsx @@ -1,17 +1,83 @@ import React from 'react' +import { StyleSheet, View } from 'react-native' +import { useHeaderMeasurements } from 'react-native-collapsible-tab-view' +import Animated, { + interpolate, + useAnimatedStyle, + useDerivedValue, +} from 'react-native-reanimated' -import ExampleComponent from './Shared/ExampleComponent' -import { buildHeader } from './Shared/Header' +import { useCurrentTabScrollY } from '../../src/hooks' +import ExampleComponent from './Shared/ExampleComponentFlashList' +import ReText from './Shared/ReText' import { ExampleComponentType } from './types' -const title = 'Lazy Example' +const title = 'FlashList (contacts tab)' -const Header = buildHeader(title) +const MIN_HEADER_HEIGHT = 48 -const DefaultExample: ExampleComponentType = () => { - return +export const Header = () => { + const { top, height } = useHeaderMeasurements() + const scrollY = useCurrentTabScrollY() + + const scrollYText = useDerivedValue( + () => `Scroll Y is: ${scrollY.value.toFixed(2)}` + ) + + const stylez = useAnimatedStyle(() => { + return { + transform: [ + { + translateY: interpolate( + top.value, + [0, -(height.value || 0 - MIN_HEADER_HEIGHT)], + [0, (height.value || 0 - MIN_HEADER_HEIGHT) / 2] + ), + }, + ], + } + }) + + return ( + + + + + + ) } -DefaultExample.title = title +const Example: ExampleComponentType = () => { + return ( +
} + minHeaderHeight={MIN_HEADER_HEIGHT} + /> + ) +} + +const styles = StyleSheet.create({ + root: { + backgroundColor: '#2196f3', + justifyContent: 'center', + alignItems: 'center', + padding: 16, + height: 250, + }, + container: { + height: MIN_HEADER_HEIGHT, + justifyContent: 'center', + alignItems: 'center', + width: '100%', + }, + text: { + position: 'absolute', + color: 'white', + fontSize: 24, + textAlign: 'center', + }, +}) + +Example.title = title -export default DefaultExample +export default Example From c41fe8b20873e9032dcac49f6e1f9b1a06165d7e Mon Sep 17 00:00:00 2001 From: "Luciano J. Bracco" Date: Fri, 21 Apr 2023 08:54:48 -0300 Subject: [PATCH 05/10] chore: added flashlist support --- documentation/README_TEMPLATE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/README_TEMPLATE.md b/documentation/README_TEMPLATE.md index 31f7754f..e7407a4b 100644 --- a/documentation/README_TEMPLATE.md +++ b/documentation/README_TEMPLATE.md @@ -17,6 +17,7 @@ - [Tabs.Container](#tabscontainer) - [Tabs.Lazy](#tabslazy) - [Tabs.FlatList](#tabsflatlist) + - [Tabs.FlashList](#tabsflatlist) - [Tabs.SectionList](#tabssectionlist) - [Tabs.ScrollView](#tabsscrollview) - [Ref](#ref) From 6886838e710e19f2624695c9777a721b25f7f929 Mon Sep 17 00:00:00 2001 From: "Luciano J. Bracco" Date: Fri, 21 Apr 2023 09:22:37 -0300 Subject: [PATCH 06/10] chore: updated documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dc5cbf22..0dc4c3dc 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ - [Tabs.Container](#tabscontainer) - [Tabs.Lazy](#tabslazy) - [Tabs.FlatList](#tabsflatlist) - - [Tabs.FlashList](#tabsflatlist) + - [Tabs.FlashList](#tabsflashlist) - [Tabs.SectionList](#tabssectionlist) - [Tabs.ScrollView](#tabsscrollview) - [Ref](#ref) From 2618bbf18517b4f0b520d146e79c81662b0f78d3 Mon Sep 17 00:00:00 2001 From: Luciano Bracco <105660442+lucianobracco-geojam@users.noreply.github.com> Date: Fri, 21 Apr 2023 09:36:46 -0300 Subject: [PATCH 07/10] Update package.json Co-authored-by: Andrei Alecu --- package.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/package.json b/package.json index fc7dae2a..7e9939f0 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,13 @@ "react-native-reanimated": ">=2.15.0", "@shopify/flash-list": ">=1.0.0" }, + { + "peerDependenciesMeta": { + "@shopify/flash-list": { + "optional": true + } + } + }, "husky": { "hooks": { "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", From ba4a4b80112c0e7127bb1a53f8f02f4eaee92416 Mon Sep 17 00:00:00 2001 From: "Luciano J. Bracco" Date: Fri, 21 Apr 2023 09:37:36 -0300 Subject: [PATCH 08/10] chore: fixed package.json suggestion --- package.json | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 7e9939f0..d12422c1 100644 --- a/package.json +++ b/package.json @@ -81,11 +81,9 @@ "react-native-reanimated": ">=2.15.0", "@shopify/flash-list": ">=1.0.0" }, - { - "peerDependenciesMeta": { - "@shopify/flash-list": { - "optional": true - } + "peerDependenciesMeta": { + "@shopify/flash-list": { + "optional": true } }, "husky": { From 5873becd325d9ff1ec4da1a9f137c76d9a2259cb Mon Sep 17 00:00:00 2001 From: "Luciano J. Bracco" Date: Fri, 21 Apr 2023 11:35:26 -0300 Subject: [PATCH 09/10] fix: updated dependencies to keep flashlist as optional --- example/ios/Podfile.lock | 6 ------ src/FlashList.tsx | 20 ++++++++++++++++++-- src/helpers.tsx | 3 --- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index a5b63460..b2b3c31e 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -335,8 +335,6 @@ PODS: - React-jsi (= 0.71.6) - React-logger (= 0.71.6) - React-perflogger (= 0.71.6) - - RNFlashList (1.4.2): - - React-Core - RNGestureHandler (2.9.0): - React-Core - RNReanimated (2.14.4): @@ -411,7 +409,6 @@ DEPENDENCIES: - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - - "RNFlashList (from `../node_modules/@shopify/flash-list`)" - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNReanimated (from `../node_modules/react-native-reanimated`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) @@ -503,8 +500,6 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" ReactCommon: :path: "../node_modules/react-native/ReactCommon" - RNFlashList: - :path: "../node_modules/@shopify/flash-list" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNReanimated: @@ -555,7 +550,6 @@ SPEC CHECKSUMS: React-RCTVibration: 73d201599a64ea14b4e0b8f91b64970979fd92e6 React-runtimeexecutor: 8692ac548bec648fa121980ccb4304afd136d584 ReactCommon: e1067159764444e5db7c14e294d5cd79fb159c59 - RNFlashList: 7fbca4fc075484a9426f1610d648dbea2de94eb0 RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39 RNReanimated: cc5e3aa479cb9170bcccf8204291a6950a3be128 Yoga: ba09b6b11e6139e3df8229238aa794205ca6a02a diff --git a/src/FlashList.tsx b/src/FlashList.tsx index 88cf9b00..f01a9f96 100644 --- a/src/FlashList.tsx +++ b/src/FlashList.tsx @@ -1,7 +1,10 @@ -import { FlashList as SPFlashList, FlashListProps } from '@shopify/flash-list' +import type { + FlashListProps, + FlashList as SPFlashList, +} from '@shopify/flash-list' import React from 'react' +import Animated from 'react-native-reanimated' -import { AnimatedFlashList } from './helpers' import { useAfterMountEffect, useChainCallback, @@ -14,6 +17,19 @@ import { useUpdateScrollViewContentSize, } from './hooks' +// Load FlashList dynamically or print a friendly error message +let AnimatedFlashList: React.ComponentClass> +try { + const flashListModule = require('@shopify/flash-list') + AnimatedFlashList = (Animated.createAnimatedComponent( + flashListModule.FlashList + ) as unknown) as React.ComponentClass> +} catch (error) { + console.error( + 'The optional dependency @shopify/flash-list is not installed. Please install it to use the FlashList component.' + ) +} + /** * Used as a memo to prevent rerendering too often when the context changes. * See: https://github.com/facebook/react/issues/15156#issuecomment-474590693 diff --git a/src/helpers.tsx b/src/helpers.tsx index 7a1cde49..4a6b572f 100644 --- a/src/helpers.tsx +++ b/src/helpers.tsx @@ -1,4 +1,3 @@ -import { FlashList } from '@shopify/flash-list' import { FlatList, Platform, SectionList, I18nManager } from 'react-native' import Animated, { scrollTo } from 'react-native-reanimated' @@ -29,5 +28,3 @@ export function scrollToImpl( scrollTo(ref, x, y, animated) } - -export const AnimatedFlashList = Animated.createAnimatedComponent(FlashList) From 98ed9406f9953dc01858dafabf57223075fded96 Mon Sep 17 00:00:00 2001 From: "Luciano J. Bracco" Date: Fri, 21 Apr 2023 11:47:28 -0300 Subject: [PATCH 10/10] fix: dependency being required when using specific list implementation --- example/ios/Podfile.lock | 6 ++++++ src/FlashList.tsx | 27 +++++++++++++-------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index b2b3c31e..a5b63460 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -335,6 +335,8 @@ PODS: - React-jsi (= 0.71.6) - React-logger (= 0.71.6) - React-perflogger (= 0.71.6) + - RNFlashList (1.4.2): + - React-Core - RNGestureHandler (2.9.0): - React-Core - RNReanimated (2.14.4): @@ -409,6 +411,7 @@ DEPENDENCIES: - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - "RNFlashList (from `../node_modules/@shopify/flash-list`)" - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNReanimated (from `../node_modules/react-native-reanimated`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) @@ -500,6 +503,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" ReactCommon: :path: "../node_modules/react-native/ReactCommon" + RNFlashList: + :path: "../node_modules/@shopify/flash-list" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNReanimated: @@ -550,6 +555,7 @@ SPEC CHECKSUMS: React-RCTVibration: 73d201599a64ea14b4e0b8f91b64970979fd92e6 React-runtimeexecutor: 8692ac548bec648fa121980ccb4304afd136d584 ReactCommon: e1067159764444e5db7c14e294d5cd79fb159c59 + RNFlashList: 7fbca4fc075484a9426f1610d648dbea2de94eb0 RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39 RNReanimated: cc5e3aa479cb9170bcccf8204291a6950a3be128 Yoga: ba09b6b11e6139e3df8229238aa794205ca6a02a diff --git a/src/FlashList.tsx b/src/FlashList.tsx index f01a9f96..011ad255 100644 --- a/src/FlashList.tsx +++ b/src/FlashList.tsx @@ -17,19 +17,6 @@ import { useUpdateScrollViewContentSize, } from './hooks' -// Load FlashList dynamically or print a friendly error message -let AnimatedFlashList: React.ComponentClass> -try { - const flashListModule = require('@shopify/flash-list') - AnimatedFlashList = (Animated.createAnimatedComponent( - flashListModule.FlashList - ) as unknown) as React.ComponentClass> -} catch (error) { - console.error( - 'The optional dependency @shopify/flash-list is not installed. Please install it to use the FlashList component.' - ) -} - /** * Used as a memo to prevent rerendering too often when the context changes. * See: https://github.com/facebook/react/issues/15156#issuecomment-474590693 @@ -40,7 +27,19 @@ type FlashListMemoRef = SPFlashList const FlashListMemo = React.memo( React.forwardRef((props, passRef) => { - return + // Load FlashList dynamically or print a friendly error message + try { + const flashListModule = require('@shopify/flash-list') + const AnimatedFlashList = (Animated.createAnimatedComponent( + flashListModule.FlashList + ) as unknown) as React.ComponentClass> + return + } catch (error) { + console.error( + 'The optional dependency @shopify/flash-list is not installed. Please install it to use the FlashList component.' + ) + return <> + } }) )