Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(boilerplate) @shopify/flash-list integration #2513

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ jobs:
resource_class: large
steps:
- checkout
- bun-orb/setup
- bun-orb/setup:
version: 1.0.3
- restore_cache:
name: Restore node modules
keys:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Nothing makes it into Ignite unless it's been proven on projects that Infinite R
| Jest | Test Runner | v26 | Standard test runner for JS apps |
| Maestro | Testing Framework | | Automate end-to-end UI testing |
| date-fns | Date library | v2 | Excellent date library |
| FlashList | FlatList replacement | v1 | A performant drop-in replacement for FlatList |

Ignite also comes with a [component library](https://github.com/infinitered/ignite/blob/master/docs/Components.md) that is tuned for custom designs, theming support, testing, custom fonts, generators, and much, much more.

Expand Down
44 changes: 44 additions & 0 deletions boilerplate/app/components/ListView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, { forwardRef, PropsWithoutRef } from "react"
import { FlatList } from "react-native"
import { isRTL } from "app/i18n"
import { FlashList, FlashListProps } from "@shopify/flash-list"

export type ListViewRef<T> = FlashList<T> | FlatList<T>

export type ListViewProps<T> = PropsWithoutRef<FlashListProps<T>>

/**
* This is a Higher Order Component meant to ease the pain of using @shopify/flash-list
* when there is a chance that a user would have their device language set to an
* RTL language like Arabic or Punjabi. This component will use react-native's
* FlatList if the user's language is RTL or FlashList if the user's language is LTR.
*
* Because FlashList's props are a superset of FlatList's, you must pass estimatedItemSize
* to this component if you want to use it.
*
* This is a temporary workaround until the FlashList component supports RTL at
* which point this component can be removed and we will default to using FlashList everywhere.
*
* @see {@link https://github.com/Shopify/flash-list/issues/544|RTL Bug Android}
* @see {@link https://github.com/Shopify/flash-list/issues/840|Flashlist Not Support RTL}
*
* @param props - FlashListProps | FlatListProps
* @param forwardRef - React.Ref<ListProps<T>>
* @returns JSX.Element
*/

const ListViewComponent = forwardRef(
<T,>(props: ListViewProps<T>, ref: React.ForwardedRef<ListViewRef<T>>) => {
const ListComponentWrapper = isRTL ? FlatList : FlashList

return <ListComponentWrapper {...props} ref={ref} />
},
)

ListViewComponent.displayName = "ListView"

export const ListView = ListViewComponent as <T>(
props: ListViewProps<T> & {
ref?: React.RefObject<ListViewRef<T>>
},
) => React.ReactElement
1 change: 1 addition & 0 deletions boilerplate/app/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from "./Card"
export * from "./Header"
export * from "./Icon"
export * from "./ListItem"
export * from "./ListView"
export * from "./Screen"
export * from "./Text"
export * from "./TextField"
Expand Down
16 changes: 7 additions & 9 deletions boilerplate/app/screens/DemoPodcastListScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
// Interested in migrating from FlatList to FlashList? Check out the recipe in our Ignite Cookbook
// https://ignitecookbook.com/docs/recipes/MigratingToFlashList
import { observer } from "mobx-react-lite"
import React, { FC, useEffect, useMemo } from "react"
import {
AccessibilityProps,
ActivityIndicator,
FlatList,
Image,
ImageStyle,
Platform,
Expand All @@ -14,14 +11,15 @@ import {
View,
ViewStyle,
} from "react-native"
import { type ContentStyle } from "@shopify/flash-list"
import Animated, {
Extrapolate,
interpolate,
useAnimatedStyle,
useSharedValue,
withSpring,
} from "react-native-reanimated"
import { Button, Card, EmptyState, Icon, Screen, Text, Toggle } from "../components"
import { Button, Card, EmptyState, Icon, ListView, Screen, Text, Toggle } from "../components"
import { isRTL, translate } from "../i18n"
import { useStores } from "../models"
import { Episode } from "../models/Episode"
Expand Down Expand Up @@ -66,11 +64,12 @@ export const DemoPodcastListScreen: FC<DemoTabScreenProps<"DemoPodcastList">> =
safeAreaEdges={["top"]}
contentContainerStyle={$screenContentContainer}
>
<FlatList<Episode>
data={episodeStore.episodesForList}
<ListView<Episode>
contentContainerStyle={$listContentContainer}
data={episodeStore.episodesForList.slice()}
extraData={episodeStore.favorites.length + episodeStore.episodes.length}
contentContainerStyle={$flatListContentContainer}
refreshing={refreshing}
estimatedItemSize={177}
onRefresh={manualRefresh}
ListEmptyComponent={
isLoading ? (
Expand Down Expand Up @@ -118,7 +117,6 @@ export const DemoPodcastListScreen: FC<DemoTabScreenProps<"DemoPodcastList">> =
}
renderItem={({ item }) => (
<EpisodeCard
key={item.guid}
episode={item}
isFavorite={episodeStore.hasFavorite(item)}
onPressFavorite={() => episodeStore.toggleFavorite(item)}
Expand Down Expand Up @@ -296,7 +294,7 @@ const $screenContentContainer: ViewStyle = {
flex: 1,
}

const $flatListContentContainer: ViewStyle = {
const $listContentContainer: ContentStyle = {
paddingHorizontal: spacing.lg,
paddingTop: spacing.lg + spacing.xl,
paddingBottom: spacing.lg,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Link, RouteProp, useRoute } from "@react-navigation/native"
import React, { FC, ReactElement, useEffect, useRef, useState } from "react"
import {
Dimensions,
FlatList,
Image,
ImageStyle,
Platform,
Expand All @@ -11,9 +10,10 @@ import {
View,
ViewStyle,
} from "react-native"
import { type ContentStyle } from "@shopify/flash-list"
import { DrawerLayout, DrawerState } from "react-native-gesture-handler"
import { useSharedValue, withTiming } from "react-native-reanimated"
import { ListItem, Screen, Text } from "../../components"
import { ListItem, ListView, ListViewRef, Screen, Text } from "../../components"
import { isRTL } from "../../i18n"
import { DemoTabParamList, DemoTabScreenProps } from "../../navigators/DemoNavigator"
import { colors, spacing } from "../../theme"
Expand Down Expand Up @@ -88,7 +88,7 @@ export const DemoShowroomScreen: FC<DemoTabScreenProps<"DemoShowroom">> =
const timeout = useRef<ReturnType<typeof setTimeout>>()
const drawerRef = useRef<DrawerLayout>(null)
const listRef = useRef<SectionList>(null)
const menuRef = useRef<FlatList>(null)
const menuRef = useRef<ListViewRef<DemoListItem["item"]>>(null)
const progress = useSharedValue(0)
const route = useRoute<RouteProp<DemoTabParamList, "DemoShowroom">>()
const params = route.params
Expand Down Expand Up @@ -181,12 +181,13 @@ export const DemoShowroomScreen: FC<DemoTabScreenProps<"DemoShowroom">> =
<Image source={logo} style={$logoImage} />
</View>

<FlatList<{ name: string; useCases: string[] }>
<ListView<DemoListItem["item"]>
ref={menuRef}
contentContainerStyle={$flatListContentContainer}
contentContainerStyle={$listContentContainer}
estimatedItemSize={250}
data={Object.values(Demos).map((d) => ({
name: d.name,
useCases: d.data.map((u) => u.props.name),
useCases: d.data.map((u) => u.props.name as string),
}))}
keyExtractor={(item) => item.name}
renderItem={({ item, index: sectionIndex }) => (
Expand Down Expand Up @@ -237,7 +238,7 @@ const $drawer: ViewStyle = {
flex: 1,
}

const $flatListContentContainer: ViewStyle = {
const $listContentContainer: ContentStyle = {
paddingHorizontal: spacing.lg,
}

Expand Down
19 changes: 8 additions & 11 deletions boilerplate/app/screens/DemoShowroomScreen/demos/DemoListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
/* eslint-disable react/jsx-key, react-native/no-inline-styles */
import React from "react"
import { TextStyle, View, ViewStyle } from "react-native"
import { FlatList } from "react-native-gesture-handler"
import { Icon, ListItem, Text } from "../../../components"
import { Icon, ListItem, ListView, Text } from "../../../components"
import { colors, spacing } from "../../../theme"
import { Demo } from "../DemoShowroomScreen"
import { DemoDivider } from "../DemoDivider"
import { DemoUseCase } from "../DemoUseCase"

const flatListData =
const listData =
`Tempor Id Ea Aliqua Pariatur Aliquip. Irure Minim Voluptate Consectetur Consequat Sint Esse Proident Irure. Nostrud Elit Veniam Nostrud Excepteur Minim Deserunt Quis Dolore Velit Nulla Irure Voluptate Tempor. Occaecat Amet Laboris Nostrud Qui Do Quis Lorem Ex Elit Fugiat Deserunt. In Pariatur Excepteur Exercitation Ex Incididunt Qui Mollit Dolor Sit Non. Culpa Officia Minim Cillum Exercitation Voluptate Proident Laboris Et Est Reprehenderit Quis Pariatur Nisi`
.split(".")
.map((item) => item.trim())
Expand Down Expand Up @@ -36,11 +35,10 @@ const $customContainerStyle: ViewStyle = {
borderTopColor: colors.palette.neutral100,
}

const $flatListStyle: ViewStyle = {
const $listStyle: ViewStyle = {
height: 148,
paddingHorizontal: spacing.xs,
backgroundColor: colors.palette.neutral200,
flex: 1,
overflow: "scroll",
}

export const DemoListItem: Demo = {
Expand Down Expand Up @@ -145,13 +143,12 @@ export const DemoListItem: Demo = {
</DemoUseCase>,

<DemoUseCase
name="Integrating w/ FlatList"
name="Integrating w/ FlatList & FlashList"
description="The component can be easily integrated with your favorite list interface."
>
<View style={{ height: 148 }}>
<FlatList<string>
data={flatListData}
style={$flatListStyle}
<View style={$listStyle}>
<ListView<string>
data={listData}
renderItem={({ item, index }) => (
<ListItem
text={item}
Expand Down
1 change: 1 addition & 0 deletions boilerplate/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@react-navigation/bottom-tabs": "^6.3.2",
"@react-navigation/native": "^6.0.2",
"@react-navigation/native-stack": "^6.0.2",
"@shopify/flash-list": "^1.6.1",
"apisauce": "3.0.1",
"date-fns": "^2.30.0",
"expo": "^49.0.13",
Expand Down
14 changes: 14 additions & 0 deletions boilerplate/react-native.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,18 @@ module.exports = {
// packages, you can add them to the referenced path below and uncomment this line.
// "./assets/fonts/"
],

// This prevents FlashList from rendering a large red box.
// See: https://github.com/reactwg/react-native-new-architecture/discussions/135
// Remove when FlashList fully supports the new architecture.
// https://github.com/Shopify/flash-list/pull/550
//
project: {
android: {
unstable_reactLegacyComponentNames: ["CellContainer", "AutoLayoutView"],
},
ios: {
unstable_reactLegacyComponentNames: ["CellContainer", "AutoLayoutView"],
},
},
}
13 changes: 13 additions & 0 deletions docs/Components-ListView.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# ListView Component

The `ListView` component is a Higher Order Component that uses [Shopify's FlashList](https://shopify.github.io/flash-list/) component to display a list unless the user's preferred language is RTL (right-to-left). This is because FlashList has [known](https://github.com/Shopify/flash-list/issues/544) [issues](https://github.com/Shopify/flash-list/issues/840) with RTL support. Once these issues with FlashList are resolved, this component will be deprecated and FlashList will be used directly.

## Props

The `ListView` component uses props from `FlashList` because they are a superset of `FlatList` and only require one extra prop to work for both component types.

> [Please see the FlashList documentation for more information on the props that are available.](https://shopify.github.io/flash-list/docs/usage)

### `estimatedItemSize`

> [Please see the FlashList documentation for more information on this prop.](https://shopify.github.io/flash-list/docs/estimated-item-size)
1 change: 1 addition & 0 deletions docs/Folder-Structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ This is where your components will live, the reusable building blocks to create
- [Header](./Components-Header.md)
- [Icon](./Components-Icon.md)
- [ListItem](./Components-ListItem.md)
- [ListView](./Components-ListView.md)
- [Screen](./Components-Screen.md)
- [Text](./Components-Text.md)
- [TextField](./Components-TextField.md)
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Check out this list of topics:
- [Header](./Components-Header.md)
- [Icon](./Components-Icon.md)
- [ListItem](./Components-ListItem.md)
- [ListView](./Components-ListView.md)
- [Screen](./Components-Screen.md)
- [Text](./Components-Text.md)
- [TextField](./Components-TextField.md)
Expand Down
1 change: 0 additions & 1 deletion src/commands/new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,6 @@ module.exports = {
startSpinner(" Enabling New Architecture")
try {
const appJsonRaw = read("app.json")

const appJson = JSON.parse(appJsonRaw)
appJson.expo.plugins[1][1].ios.newArchEnabled = true
appJson.expo.plugins[1][1].android.newArchEnabled = true
Expand Down
1 change: 1 addition & 0 deletions src/tools/expoGoCompatibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const expoGoCompatExpectedVersions = {
"expo-file-system": "~15.4.4",
"expo-font": "~11.4.0",
"expo-localization": "~14.3.0",
"@shopify/flash-list": "1.4.3",
frankcalise marked this conversation as resolved.
Show resolved Hide resolved
}

// This function takes a package.json file as a string and updates the versions of the
Expand Down
2 changes: 2 additions & 0 deletions test/vanilla/ignite-generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ describe("ignite-cli generate", () => {
export * from \\"./Header\\"
export * from \\"./Icon\\"
export * from \\"./ListItem\\"
export * from \\"./ListView\\"
export * from \\"./Screen\\"
export * from \\"./Text\\"
export * from \\"./TextField\\"
Expand Down Expand Up @@ -342,6 +343,7 @@ describe("ignite-cli generate", () => {
export * from \\"./Header\\"
export * from \\"./Icon\\"
export * from \\"./ListItem\\"
export * from \\"./ListView\\"
export * from \\"./Screen\\"
export * from \\"./Text\\"
export * from \\"./TextField\\"
Expand Down