When implementing React hooks around navigation focus or React Suspense:
1) Treat focus/mount as distinct
useFocusEffect to run data refetches, remember the callback also runs on initial mount. Skip the first run when appropriate.2) For Suspense + prefetch, don’t “prefetch with suspense”
usePrefetchQuery / usePrefetchInfiniteQuery) rather than forcing suspense queries just to prefetch.3) Avoid re-renders for non-UI-affecting query observers
notifyOnChangeProps: []).4) Don’t expect placeholderData to work with useSuspenseQuery
startTransition when changing query keys to avoid unwanted fallback churn.Example (focus refetch with correct skip, plus cache-warming prefetch without suspense):
import { useFocusEffect } from '@react-navigation/native'
import { useQueryClient } from '@tanstack/react-query'
function useRefreshOnFocus(refetch: () => Promise<unknown>) {
const queryClient = useQueryClient()
useFocusEffect(
// If you keep local “first run” logic, ensure you skip the initial mount run.
(callback) => {
// callback pattern used by React Navigation; you can also use a ref.
let isFirst = true
const run = async () => {
if (isFirst) {
isFirst = false
return
}
// Prefer explicit query behavior rather than always invoking refetch()
queryClient.refetchQueries({ queryKey: ['posts'], stale: true, type: 'active' })
}
run()
return callback()
},
[queryClient]
)
}
Key checklist before approving PRs:
useFocusEffect logic explicitly handle the initial mount run?notifyOnChangeProps: [])?startTransition for query-key updates instead of relying on placeholderData with useSuspenseQuery?