PostThread perf: reduce time wasted on rendering skeletons (#10092)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import {memo, useCallback, useMemo} from 'react'
|
||||
import {memo, useMemo} from 'react'
|
||||
import {Text as RNText, View} from 'react-native'
|
||||
import {
|
||||
AppBskyFeedDefs,
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from '@atproto/api'
|
||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
@@ -245,7 +246,7 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
|
||||
}
|
||||
}, [postSource])
|
||||
|
||||
const onPressReply = useCallback(() => {
|
||||
const onPressReply = useNonReactiveCallback(() => {
|
||||
openComposer({
|
||||
replyTo: {
|
||||
uri: post.uri,
|
||||
@@ -268,15 +269,7 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
|
||||
reqId: postSource.post.reqId,
|
||||
})
|
||||
}
|
||||
}, [
|
||||
openComposer,
|
||||
post,
|
||||
record,
|
||||
onPostSuccess,
|
||||
moderation,
|
||||
postSource,
|
||||
feedFeedback,
|
||||
])
|
||||
})
|
||||
|
||||
const onOpenAuthor = () => {
|
||||
ax.metric('post:clickthroughAuthor', {
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
||||
import {
|
||||
startTransition,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import {useWindowDimensions, View} from 'react-native'
|
||||
import Animated, {useAnimatedStyle} from 'react-native-reanimated'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
import {usePostViewTracking} from '#/lib/hooks/usePostViewTracking'
|
||||
import {useFeedFeedback} from '#/state/feed-feedback'
|
||||
@@ -98,7 +106,7 @@ export function PostThread({uri}: {uri: string}) {
|
||||
const trackThreadItemView = usePostViewTracking('PostThreadItem')
|
||||
|
||||
const {openComposer} = useOpenComposer()
|
||||
const optimisticOnPostReply = useCallback(
|
||||
const optimisticOnPostReply = useNonReactiveCallback(
|
||||
(payload: OnPostSuccessData) => {
|
||||
if (payload) {
|
||||
const {replyToUri, posts} = payload
|
||||
@@ -107,9 +115,8 @@ export function PostThread({uri}: {uri: string}) {
|
||||
}
|
||||
}
|
||||
},
|
||||
[thread],
|
||||
)
|
||||
const onReplyToAnchor = useCallback(() => {
|
||||
const onReplyToAnchor = useNonReactiveCallback(() => {
|
||||
if (anchor?.type !== 'threadPost') {
|
||||
return
|
||||
}
|
||||
@@ -136,13 +143,7 @@ export function PostThread({uri}: {uri: string}) {
|
||||
reqId: anchorPostSource.post.reqId,
|
||||
})
|
||||
}
|
||||
}, [
|
||||
anchor,
|
||||
openComposer,
|
||||
optimisticOnPostReply,
|
||||
anchorPostSource,
|
||||
feedFeedback,
|
||||
])
|
||||
})
|
||||
|
||||
const isRoot = !!anchor && anchor.value.post.record.reply === undefined
|
||||
const canReply = !anchor?.value.post?.viewer?.replyDisabled
|
||||
@@ -389,12 +390,34 @@ export function PostThread({uri}: {uri: string}) {
|
||||
return results
|
||||
}, [thread, deferParents, maxParentCount, maxChildrenCount])
|
||||
|
||||
/**
|
||||
* Defer rendering reply skeletons so that the anchor post (from cache)
|
||||
* can paint without being blocked by skeleton layout work. On mount,
|
||||
* skeletons are filtered out. After the first render, they're added
|
||||
* back via a low-priority transition.
|
||||
*/
|
||||
const [showReplySkeletons, setShowReplySkeletons] = useState(false)
|
||||
useEffect(() => {
|
||||
if (thread.state.isPlaceholderData && !showReplySkeletons) {
|
||||
startTransition(() => {
|
||||
setShowReplySkeletons(true)
|
||||
})
|
||||
}
|
||||
}, [thread.state.isPlaceholderData, showReplySkeletons])
|
||||
|
||||
const deferredSlices = useMemo(() => {
|
||||
if (showReplySkeletons) return slices
|
||||
return slices.filter(
|
||||
item => !(item.type === 'skeleton' && item.item === 'reply'),
|
||||
)
|
||||
}, [slices, showReplySkeletons])
|
||||
|
||||
const isTombstoneView = useMemo(() => {
|
||||
if (slices.length > 1) return false
|
||||
return slices.every(
|
||||
if (deferredSlices.length > 1) return false
|
||||
return deferredSlices.every(
|
||||
s => s.type === 'threadPostBlocked' || s.type === 'threadPostNotFound',
|
||||
)
|
||||
}, [slices])
|
||||
}, [deferredSlices])
|
||||
|
||||
const renderItem = useCallback(
|
||||
({item, index}: {item: ThreadItem; index: number}) => {
|
||||
@@ -547,7 +570,7 @@ export function PostThread({uri}: {uri: string}) {
|
||||
) : (
|
||||
<List
|
||||
ref={listRef}
|
||||
data={slices}
|
||||
data={deferredSlices}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={keyExtractor}
|
||||
onContentSizeChange={platform({
|
||||
@@ -594,8 +617,10 @@ export function PostThread({uri}: {uri: string}) {
|
||||
initialNumToRender={initialNumToRender}
|
||||
/**
|
||||
* Default: 21
|
||||
*
|
||||
* Smaller for placeholder data so we don't waste time rendering skeletons
|
||||
*/
|
||||
windowSize={7}
|
||||
windowSize={thread.state.isPlaceholderData ? 1 : 7}
|
||||
/**
|
||||
* Default: 10
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user