remove list jitter by initially preferring ssr

This commit is contained in:
keyan 2023-08-06 13:04:25 -05:00
parent eeaf6e10e5
commit 90f4d41fc8
5 changed files with 47 additions and 14 deletions

View File

@ -8,19 +8,21 @@ import { CommentFlat } from './comment'
import { SUB_ITEMS } from '../fragments/subs'
import { LIMIT } from '../lib/cursor'
import ItemFull from './item-full'
import { useData } from './use-data'
export default function Items ({ ssrData, variables = {}, query, destructureData, rank, noMoreText, Footer, filter = () => true }) {
const { data, fetchMore } = useQuery(query || SUB_ITEMS, { variables })
const Foooter = Footer || MoreFooter
const dat = useData(data, ssrData)
const { items, pins, cursor } = useMemo(() => {
if (!data && !ssrData) return {}
if (!dat) return {}
if (destructureData) {
return destructureData(data || ssrData)
return destructureData(dat)
} else {
return data?.items || ssrData?.items
return dat?.items
}
}, [data, ssrData])
}, [dat])
const pinMap = useMemo(() =>
pins?.reduce((a, p) => { a[p.position] = p; return a }, {}), [pins])
@ -28,7 +30,7 @@ export default function Items ({ ssrData, variables = {}, query, destructureData
const Skeleton = useCallback(() =>
<ItemsSkeleton rank={rank} startRank={items?.length} limit={variables.limit} />, [rank, items])
if (!ssrData && !data) {
if (!dat) {
return <Skeleton />
}

View File

@ -20,6 +20,7 @@ import styles from './notifications.module.css'
import { useServiceWorker } from './serviceworker'
import { Checkbox, Form } from './form'
import { useRouter } from 'next/router'
import { useData } from './use-data'
function Notification ({ n, fresh }) {
const type = n.__typename
@ -340,10 +341,11 @@ const nid = n => n.__typename + n.id + n.sortTime
export default function Notifications ({ ssrData }) {
const { data, fetchMore } = useQuery(NOTIFICATIONS)
const router = useRouter()
const dat = useData(data, ssrData)
const { notifications: { notifications, lastChecked, cursor } } = useMemo(() => {
return data || ssrData || { notifications: {} }
}, [data, ssrData])
return dat || { notifications: {} }
}, [dat])
useEffect(() => {
if (lastChecked && !router?.query?.checkedAt) {
@ -358,7 +360,7 @@ export default function Notifications ({ ssrData }) {
}
}, [router, lastChecked])
if (!data && !ssrData) return <CommentsFlatSkeleton />
if (!dat) return <CommentsFlatSkeleton />
return (
<>

View File

@ -1,5 +1,5 @@
.fresh {
background-color: rgba(0, 0, 0, 0.03);
background-color: rgba(128, 128, 128, 0.1);
border-radius: 0;
}

27
components/use-data.js Normal file
View File

@ -0,0 +1,27 @@
import { useEffect, useRef, useState } from 'react'
/*
What we want is to use ssrData if it exists, until cache data changes
... this prevents item list jitter where the intially rendered items
are stale until the cache is rewritten with incoming ssrData
*/
export function useData (data, ssrData) {
// when fresh is true, it means data has been updated after the initial render and it's populated
const [fresh, setFresh] = useState(false)
// on first render, we want to use ssrData if it's available
// it's only unavailable on back/forward navigation
const ref = useRef(true)
const firstRender = ref.current
ref.current = false
useEffect(() => {
if (!firstRender && !fresh && data) setFresh(true)
}, [data])
// if we don't have data yet, use ssrData
// if we have data, but it's not fresh, use ssrData
// unless we don't have ssrData
if (!data || (!fresh && ssrData)) return ssrData
return data
}

View File

@ -7,6 +7,7 @@ import userStyles from './user-header.module.css'
import { useEffect, useMemo, useState } from 'react'
import { useQuery } from '@apollo/client'
import MoreFooter from './more-footer'
import { useData } from './use-data'
// all of this nonsense is to show the stat we are sorting by first
const Stacked = ({ user }) => (<span>{abbrNum(user.stacked)} stacked</span>)
@ -37,6 +38,7 @@ function seperate (arr, seperator) {
export default function UserList ({ ssrData, query, variables, destructureData }) {
const { data, fetchMore } = useQuery(query, { variables })
const dat = useData(data, ssrData)
const [statComps, setStatComps] = useState(seperate(STAT_COMPONENTS, Seperator))
useEffect(() => {
@ -48,15 +50,15 @@ export default function UserList ({ ssrData, query, variables, destructureData }
}, [variables?.by])
const { users, cursor } = useMemo(() => {
if (!data && !ssrData) return {}
if (!dat) return {}
if (destructureData) {
return destructureData(data || ssrData)
return destructureData(dat)
} else {
return data || ssrData
return dat
}
}, [data, ssrData])
}, [dat])
if (!ssrData && !data) {
if (!dat) {
return <UsersSkeleton />
}