From 189c06e157c742a47b002a2b856f1fb18e6b6978 Mon Sep 17 00:00:00 2001 From: Carolina Gonzalez Date: Thu, 13 Nov 2025 13:27:41 -0500 Subject: [PATCH 1/2] fix(react): ensure useDocumentProjection reuses state source for all strings --- .../DocumentProjectionRoute.tsx | 99 ++++++++++++------- .../hooks/projection/useDocumentProjection.ts | 17 +++- 2 files changed, 80 insertions(+), 36 deletions(-) diff --git a/apps/kitchensink-react/src/DocumentCollection/DocumentProjectionRoute.tsx b/apps/kitchensink-react/src/DocumentCollection/DocumentProjectionRoute.tsx index 169e321be..9d2dcf7e6 100644 --- a/apps/kitchensink-react/src/DocumentCollection/DocumentProjectionRoute.tsx +++ b/apps/kitchensink-react/src/DocumentCollection/DocumentProjectionRoute.tsx @@ -1,34 +1,51 @@ import {DocumentHandle, useDocumentProjection, usePaginatedDocuments} from '@sanity/sdk-react' import {Box, Button, Card, Flex, Label, Spinner, Stack, Text, TextInput} from '@sanity/ui' -import {defineProjection} from 'groq' +import groq, {defineProjection} from 'groq' import {JSX, ReactNode, Suspense, useRef, useState} from 'react' import {ErrorBoundary} from 'react-error-boundary' // Import the custom table components import {Table, TD, TH, TR} from '../components/TableElements' +interface PossibleAuthorProjections { + name?: string + favoriteBookTitles: string[] + bestFriend?: { + name?: string + } + role?: string + bookCount?: number + hasBooks?: boolean +} + // Component for displaying projection data with proper error handling function ProjectionData({ docHandle, - useFirstProjection, + projectionType, }: { docHandle: DocumentHandle<'author'> - useFirstProjection: boolean + projectionType: 'favoriteBooks' | 'bestFriend' | 'groqHelper' }) { - const authorProjection = defineProjection(`{ + const projections: Record = { + favoriteBooks: `{ name, "favoriteBookTitles": favoriteBooks[]->{title}.title - }`) - - const bestFriendProjection = defineProjection(`{ + }`, + bestFriend: defineProjection(`{ name, 'bestFriendName': bestFriend->{name}.name, role - }`) + }`), + groqHelper: groq`{ + name, + "bookCount": count(favoriteBooks), + "hasBooks": count(favoriteBooks) > 0 + }`, + } const ref = useRef(null) - const projection = useFirstProjection ? authorProjection : bestFriendProjection - const {data} = useDocumentProjection({ + const projection = projections[projectionType] + const {data} = useDocumentProjection({ ...docHandle, ref, projection, @@ -39,17 +56,22 @@ function ProjectionData({ {data.name || 'Untitled'} - {'favoriteBookTitles' in data ? ( + {projectionType === 'favoriteBooks' ? ( <> {data.favoriteBookTitles?.filter(Boolean).join(', ') || 'No favorite books'} - ) : ( + ) : projectionType === 'bestFriend' ? ( <> - {data.bestFriendName || 'No best friend'} + {data.bestFriend?.name || 'No best friend'} {data.role || 'No role'} + ) : ( + <> + {data.bookCount ?? 0} books + {data.hasBooks ? 'Yes' : 'No'} + )} ) @@ -84,16 +106,16 @@ function ProjectionError({error}: {error: Error}): ReactNode { // Component for displaying a single author row with projection data function AuthorRow({ docHandle, - useFirstProjection, + projectionType, }: { docHandle: DocumentHandle<'author'> - useFirstProjection: boolean + projectionType: 'favoriteBooks' | 'bestFriend' | 'groqHelper' }) { return ( }> }> - + @@ -209,7 +231,9 @@ function PaginationControls({ export function DocumentProjectionRoute(): JSX.Element { const [searchTerm, setSearchTerm] = useState('') const [pageSize, setPageSize] = useState(5) - const [useFirstProjection, setUseFirstProjection] = useState(true) + const [projectionType, setProjectionType] = useState< + 'favoriteBooks' | 'bestFriend' | 'groqHelper' + >('favoriteBooks') const { data, @@ -305,41 +329,50 @@ export function DocumentProjectionRoute(): JSX.Element { /> -