address claude review feedback

- thread subject into add mutation so starter pack cache stays fresh
- rename RQKEY to createListsWithMembershipQueryKey with object arg
- show loader footer while paginating lists
- guard empty state on resolved data
- drop unnecessary useCallback (react compiler)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-06-04 11:46:05 +03:00
parent c73cfc159b
commit 6abd7ac5bb
2 changed files with 30 additions and 33 deletions
@@ -1,4 +1,3 @@
import {useCallback} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
@@ -96,31 +95,28 @@ function ListsContent({
const listItems = data?.pages.flatMap(page => page.listsWithMembership) || [] const listItems = data?.pages.flatMap(page => page.listsWithMembership) || []
const onEndReached = useCallback(async () => { const onEndReached = async () => {
if (isFetchingNextPage || !hasNextPage || isError) return if (isFetchingNextPage || !hasNextPage || isError) return
try { try {
await fetchNextPage() await fetchNextPage()
} catch (err) { } catch (err) {
// Error handling is optional since this is just pagination // Error handling is optional since this is just pagination
} }
}, [isFetchingNextPage, hasNextPage, isError, fetchNextPage]) }
const renderItem = useCallback( const renderItem = ({item}: {item: ListWithMembership}) =>
({item}: {item: ListWithMembership}) => profile ? (
profile ? ( <ListItem
<ListItem listWithMembership={item}
listWithMembership={item} profile={profile}
profile={profile} onAdd={onAdd}
onAdd={onAdd} onRemove={onRemove}
onRemove={onRemove} />
/> ) : null
) : null,
[profile, onAdd, onRemove],
)
const onClose = useCallback(() => { const onClose = () => {
control.close() control.close()
}, [control]) }
const listHeader = ( const listHeader = (
<View <View
@@ -174,7 +170,14 @@ function ListsContent({
onEndReached={() => void onEndReached()} onEndReached={() => void onEndReached()}
onEndReachedThreshold={0.1} onEndReachedThreshold={0.1}
ListHeaderComponent={listHeader} ListHeaderComponent={listHeader}
ListEmptyComponent={<Empty />} ListFooterComponent={
isFetchingNextPage ? (
<View style={[a.align_center, a.py_lg]}>
<Loader size="lg" />
</View>
) : null
}
ListEmptyComponent={!isLoading && data ? <Empty /> : null}
style={platform({ style={platform({
web: [a.px_2xl, {minHeight: 400}], web: [a.px_2xl, {minHeight: 400}],
native: [a.px_2xl, a.pt_lg], native: [a.px_2xl, a.pt_lg],
@@ -205,6 +208,7 @@ function ListItem({
const {mutate: addMembership, isPending: isPendingAdd} = const {mutate: addMembership, isPending: isPendingAdd} =
useListMembershipAddMutation({ useListMembershipAddMutation({
subject: profile,
onSuccess: data => { onSuccess: data => {
Toast.show(l`Added to list`) Toast.show(l`Added to list`)
onAdd?.(list.uri) onAdd?.(list.uri)
@@ -249,7 +253,7 @@ function ListItem({
const isPending = isPendingAdd || isPendingRemove const isPending = isPendingAdd || isPendingRemove
const handleToggleMembership = useCallback(() => { const handleToggleMembership = () => {
if (isPending) return if (isPending) return
if (!isMember) { if (!isMember) {
@@ -268,15 +272,7 @@ function ListItem({
membershipUri: listItem.uri, membershipUri: listItem.uri,
}) })
} }
}, [ }
list.uri,
profile.did,
isMember,
listItem,
isPending,
addMembership,
removeMembership,
])
return ( return (
<View <View
+6 -5
View File
@@ -15,8 +15,9 @@ import {useAgent} from '#/state/session'
export type ListWithMembership = export type ListWithMembership =
AppBskyGraphGetListsWithMembership.ListWithMembership AppBskyGraphGetListsWithMembership.ListWithMembership
const RQKEY_ROOT = 'lists-with-membership' const listsWithMembershipQueryKeyRoot = 'lists-with-membership'
export const RQKEY = (actor: string) => createQueryKey(RQKEY_ROOT, {actor}) export const createListsWithMembershipQueryKey = (args: {actor: string}) =>
createQueryKey(listsWithMembershipQueryKeyRoot, args)
export function useListsWithMembershipQuery({ export function useListsWithMembershipQuery({
actor, actor,
@@ -34,7 +35,7 @@ export function useListsWithMembershipQuery({
QueryKey, QueryKey,
string | undefined string | undefined
>({ >({
queryKey: RQKEY(actor ?? ''), queryKey: createListsWithMembershipQueryKey({actor: actor ?? ''}),
queryFn: async ({pageParam}: {pageParam?: string}) => { queryFn: async ({pageParam}: {pageParam?: string}) => {
const res = await agent.app.bsky.graph.getListsWithMembership({ const res = await agent.app.bsky.graph.getListsWithMembership({
actor: actor!, // the enabled flag prevents this from running until actor is set actor: actor!, // the enabled flag prevents this from running until actor is set
@@ -64,7 +65,7 @@ export function updateListMembershipOptimistically({
}) { }) {
queryClient.setQueryData< queryClient.setQueryData<
InfiniteData<AppBskyGraphGetListsWithMembership.OutputSchema> InfiniteData<AppBskyGraphGetListsWithMembership.OutputSchema>
>(RQKEY(actor), old => { >(createListsWithMembershipQueryKey({actor}), old => {
if (!old) return old if (!old) return old
return { return {
@@ -99,7 +100,7 @@ export function removeListMembershipOptimistically({
}) { }) {
queryClient.setQueryData< queryClient.setQueryData<
InfiniteData<AppBskyGraphGetListsWithMembership.OutputSchema> InfiniteData<AppBskyGraphGetListsWithMembership.OutputSchema>
>(RQKEY(actor), old => { >(createListsWithMembershipQueryKey({actor}), old => {
if (!old) return old if (!old) return old
return { return {