Skip to content

Commit

Permalink
Prefetch event schedule data to improve initial load speed (#108)
Browse files Browse the repository at this point in the history
* Update prettier to 2.8.7

* Run `yarn format`

* Seperate hooks and queryOptions, add prefetchScheduledEvents

* Add prefetchScheduledEvents to WelcomeScreen

* Add webflowOptions,  add CollectionConst type
  • Loading branch information
joshuayoes authored Apr 21, 2023
1 parent dde9fea commit 5d05575
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 71 deletions.
2 changes: 1 addition & 1 deletion app/screens/ExploreScreen/ExploreScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { groupBy } from "../../utils/groupBy"
import { customSortObjectKeys } from "../../utils/customSort"

const recommendationTypes = Object.values(WEBFLOW_MAP.recommendationType)
type RecommendationType = typeof recommendationTypes[number]
type RecommendationType = (typeof recommendationTypes)[number]
type GroupedRecommendations = Record<RecommendationType, RawRecommendations[]>

const initialRecs = recommendationTypes.reduce<GroupedRecommendations>(
Expand Down
2 changes: 1 addition & 1 deletion app/screens/InfoScreen/OurSponsorsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { SponsorCard } from "./SponsorCard"
import { groupBy } from "../../utils/groupBy"

const sponsorTiers = Object.values(WEBFLOW_MAP.sponsorTier)
type Tiers = typeof sponsorTiers[number]
type Tiers = (typeof sponsorTiers)[number]

const initialTiers = sponsorTiers.reduce<Record<Tiers, RawSponsor[]>>(
(acc, tier) => ({ ...acc, [tier]: [] }),
Expand Down
7 changes: 6 additions & 1 deletion app/screens/WelcomeScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from "react"
import React, { useLayoutEffect } from "react"
import { Dimensions, Image, ImageStyle, Platform, TextStyle, View, ViewStyle } from "react-native"
import { SafeAreaView } from "react-native-safe-area-context"
import { Button, Screen, Text } from "../components"
import { useAppNavigation } from "../hooks"
import { AppStackScreenProps } from "../navigators"
import { colors, spacing } from "../theme"
import { prefetchScheduledEvents } from "../services/api"

const welcomeLogo = require("../../assets/images/welcome-shapes.png")
const { width: screenWidth } = Dimensions.get("screen")
Expand All @@ -18,6 +19,10 @@ export const WelcomeScreen: React.FC<WelcomeScreenProps> = (_props) => {
navigation.navigate("Tabs", { screen: "Schedule" })
}

useLayoutEffect(() => {
prefetchScheduledEvents()
}, [])

return (
<Screen style={$container}>
<View style={$topContainer}>
Expand Down
118 changes: 63 additions & 55 deletions app/services/api/webflow-api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useQuery } from "@tanstack/react-query"
import { useQueries, useQuery, UseQueryOptions } from "@tanstack/react-query"
import { Schedule } from "../../screens"
import { axiosInstance, PaginatedItems } from "./axios"
import type {
Expand All @@ -13,6 +13,7 @@ import type {
RawWorkshop,
} from "./webflow-api.types"
import {
CollectionConst,
RECOMMENDATIONS,
RECURRING_EVENTS,
SCHEDULE,
Expand All @@ -31,76 +32,83 @@ import {
cleanedWorkshops,
convertScheduleToScheduleCard,
} from "./webflow-helpers"
import { queryClient } from "./react-query"

const useWebflowAPI = <T>(key: string, collectionId: string, enabled = true) =>
useQuery({
queryKey: [key],
queryFn: async () => {
const { data } = await axiosInstance.get<PaginatedItems<T>>(
`/collections/${collectionId}/items`,
)
return data.items
},
enabled,
})

export const useRecommendations = () => {
return useWebflowAPI<RawRecommendations>(RECOMMENDATIONS.key, RECOMMENDATIONS.collectionId)
const getCollectionById = async <T>(collectionId: string) => {
const { data } = await axiosInstance.get<PaginatedItems<T>>(`/collections/${collectionId}/items`)
return data.items
}

export const useRecurringEvents = () => {
return useWebflowAPI<RawRecurringEvents>(RECURRING_EVENTS.key, RECURRING_EVENTS.collectionId)
}
const webflowOptions = <Payload, Collection extends CollectionConst = CollectionConst>(
collection: Collection,
) =>
({
queryKey: [collection.key, collection.collectionId] as const,
queryFn: async () => getCollectionById<Payload>(collection.collectionId),
} satisfies UseQueryOptions)

export const useSpeakers = () => {
return useWebflowAPI<RawSpeaker>(SPEAKERS.key, SPEAKERS.collectionId)
}
const recommendationsOptions = webflowOptions<RawRecommendations>(RECOMMENDATIONS)
export const useRecommendations = () => useQuery(recommendationsOptions)

export const useSpeakerNames = () => {
return useWebflowAPI<RawSpeakerName>(SPEAKER_NAMES.key, SPEAKER_NAMES.collectionId)
}
const recurringEventsOptions = webflowOptions<RawRecurringEvents>(RECURRING_EVENTS)
export const useRecurringEvents = () => useQuery(recurringEventsOptions)

export const useSponsors = (): { isLoading: boolean; data: RawSponsor[] } => {
const { data: sponsors, isLoading } = useWebflowAPI<RawSponsor>(
SPONSORS.key,
SPONSORS.collectionId,
)
const speakersOptions = webflowOptions<RawSpeaker>(SPEAKERS)
export const useSpeakers = () => useQuery(speakersOptions)

const speakerNamesOptions = webflowOptions<RawSpeakerName>(SPEAKER_NAMES)
export const useSpeakerNames = () => useQuery(speakerNamesOptions)

const sponsorsOptions = webflowOptions<RawSponsor>(SPONSORS)
export const useSponsors = () => {
const { data: sponsors, ...rest } = useQuery(sponsorsOptions)
const data = cleanedSponsors(sponsors)

return { isLoading, data }
}
export const useTalks = () => {
return useWebflowAPI<RawTalk>(TALKS.key, TALKS.collectionId)
return { data, ...rest }
}

export const useVenues = () => {
return useWebflowAPI<RawVenue>(VENUES.key, VENUES.collectionId)
}
const talksOptions = webflowOptions<RawTalk>(TALKS)
export const useTalks = () => useQuery(talksOptions)

export const useWorkshops = () => {
return useWebflowAPI<RawWorkshop>(WORKSHOPS.key, WORKSHOPS.collectionId)
}
const venuesOptions = webflowOptions<RawVenue>(VENUES)
export const useVenues = () => useQuery(venuesOptions)

export const useScheduledEvents = () => {
const { data: speakers, isLoading } = useSpeakers()
const { data: workshops } = useWorkshops()
const { data: recurringEvents } = useRecurringEvents()
const { data: talks } = useTalks()
const { data: scheduledEvents, ...rest } = useWebflowAPI<RawScheduledEvent>(
SCHEDULE.key,
SCHEDULE.collectionId,
!isLoading && !!speakers && !!workshops && !!recurringEvents && !!talks,
const workshopsOptions = webflowOptions<RawWorkshop>(WORKSHOPS)
export const useWorkshops = () => useQuery(workshopsOptions)

const scheduledEventsOptions = webflowOptions<RawScheduledEvent>(SCHEDULE)
const scheduledEventsQueries = [
speakersOptions,
workshopsOptions,
recurringEventsOptions,
talksOptions,
scheduledEventsOptions,
] as const
export const prefetchScheduledEvents = async () => {
await Promise.all(
scheduledEventsQueries.map(async (query) => {
return queryClient.prefetchQuery(query)
}),
)
}
export const useScheduledEvents = () => {
const queries = useQueries({
queries: scheduledEventsQueries,
})

const [speakers, workshops, recurringEvents, talks, scheduledEvents] = queries

return {
data: cleanedSchedule({
recurringEvents,
scheduledEvents,
speakers: cleanedSpeakers(speakers),
talks: cleanedTalks({ speakers, talks }),
workshops: cleanedWorkshops(workshops, cleanedSpeakers(speakers)),
recurringEvents: recurringEvents.data,
scheduledEvents: scheduledEvents.data,
speakers: cleanedSpeakers(speakers.data),
talks: cleanedTalks({ speakers: speakers.data, talks: talks.data }),
workshops: cleanedWorkshops(workshops.data, cleanedSpeakers(speakers.data)),
}),
...rest,
refetch: async () => Promise.all(queries.map((query) => query.refetch())),
isLoading: queries.map((query) => query.isLoading).some((isLoading) => isLoading),
isRefetching: queries.map((query) => query.isFetching).some((isFetching) => isFetching),
}
}

Expand All @@ -126,7 +134,7 @@ export const useScheduleScreenData = () => {
title: "Conference Day 2",
events: convertScheduleToScheduleCard(events, "Friday"),
},
] as Schedule[],
] satisfies Schedule[],
refetch,
}
}
Expand Down
28 changes: 20 additions & 8 deletions app/services/api/webflow-consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ export const SITE_ID = "5ca38f35db5d2ea94aea469d"
export const SPONSORS = {
collectionId: "640a728fc24f8e73575fe189",
key: "sponsors",
}
} as const

export const SPEAKERS = {
collectionId: "640a728fc24f8e94385fe188",
key: "speakers",
}
} as const

export const SPEAKER_NAMES = {
collectionId: "640a728fc24f8e74d05fe18a",
Expand All @@ -23,32 +23,44 @@ export const WORKSHOPS = {
export const SCHEDULE = {
collectionId: "640a728fc24f8e63325fe185",
key: "schedule",
}
} as const

export const PAST_TALKS = {
collectionId: "640a728fc24f8e76ef5fe186",
key: "pastTalks",
}
} as const

export const RECURRING_EVENTS = {
collectionId: "640a728fc24f8e85a75fe18c",
key: "recurringEvents",
}
} as const

export const TALKS = {
collectionId: "640a728fc24f8e31ee5fe18e",
key: "talks",
}
} as const

export const VENUES = {
collectionId: "640a728fc24f8e553c5fe18d",
key: "venues",
}
} as const

export const RECOMMENDATIONS = {
collectionId: "640a728fc24f8e083b5fe18f",
key: "recommendations",
}
} as const

export type CollectionConst =
| typeof SPONSORS
| typeof SPEAKERS
| typeof SPEAKER_NAMES
| typeof WORKSHOPS
| typeof SCHEDULE
| typeof PAST_TALKS
| typeof RECURRING_EVENTS
| typeof TALKS
| typeof VENUES
| typeof RECOMMENDATIONS

// [NOTE] these keys probably have to change when webflow is updated
// `/collections/${collectionId}` api will the keys
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
"mocha": "6",
"patch-package": "6.4.7",
"postinstall-prepare": "1.0.1",
"prettier": "2.6.2",
"prettier": "2.8.7",
"query-string": "^7.0.1",
"react-devtools-core": "4.24.7",
"reactotron-core-client": "^2.8.10",
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11621,10 +11621,10 @@ prelude-ls@~1.1.2:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==

prettier@2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032"
integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==
prettier@2.8.7:
version "2.8.7"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450"
integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==

[email protected]:
version "5.6.0"
Expand Down

0 comments on commit 5d05575

Please sign in to comment.