Lesson
11
useUser
Log in to watch a video walkthrough of this lesson
Log in

Filter the queried list of documents based on the current user and other selections.
Log in to mark your progress for each Lesson and Task
Now that feedback documents can be marked as assigned to specific users, it would be useful to filter the feedback list of documents to just those the current user is responsible for.
The useDocuments
hook you setup initially in FeedbackList
only has a documentType
option set:
documentType: 'feedback'
However, this hook can also take filter
and params
options which may be dynamically updated by the application. Let's add some UI elements which will dynamically filter the list of returned documents.
Create a new component to dynamically filter documents by
status
import { Button, Grid } from "@sanity/ui"
type StatusSelectorProps = { status: string setStatus: (nextStatus: string) => void}
const STATUSES = ["All", "Pending", "Spam", "Approved"]
export function StatusSelector({ status, setStatus }: StatusSelectorProps) { return ( <Grid columns={[2, 2, 2, 4]} gap={1}> {STATUSES.map((statusOption) => ( <Button key={statusOption} mode={statusOption.toLowerCase() === status ? "default" : "ghost"} onClick={() => setStatus(statusOption.toLowerCase())} text={statusOption} /> ))} </Grid> )}
Create another document to toggle an additional filter for the
assignee
field.import { Switch, Inline, Text, Card } from "@sanity/ui"import { useCurrentUser } from "@sanity/sdk-react"import { Dispatch, SetStateAction } from "react"
type OnlyMineProps = { userId: string | null setUserId: Dispatch<SetStateAction<string | null>>}
export function OnlyMine({ userId, setUserId }: OnlyMineProps) { const currentUser = useCurrentUser()
return ( <Card border padding={2}> <Inline space={2}> <Text size={1} as="label" htmlFor="only-mine"> Only mine </Text> <Switch id="only-mine" disabled={!currentUser} checked={userId === currentUser?.id} onClick={() => { if (currentUser) { setUserId((currentId) => currentId === currentUser.id ? null : currentUser.id ) } }} /> </Inline> </Card> )}
Now you'll need to import these into the FeedbackList
and set a filter
that will conditionally use the params
.
Update the
FeedbackList
componentimport { Suspense, useState } from "react"import { type DocumentHandle, useDocuments } from "@sanity/sdk-react"import { Stack, Button, Spinner } from "@sanity/ui"
import { FeedbackPreview } from "./FeedbackPreview"import { StatusSelector } from "./StatusSelector"import { OnlyMine } from "./OnlyMine"
type FeedbackListProps = { selectedFeedback: DocumentHandle | null setSelectedFeedback: (feedback: DocumentHandle | null) => void}
export function FeedbackList({ selectedFeedback, setSelectedFeedback,}: FeedbackListProps) { const [userId, setUserId] = useState<string | null>(null) const [status, setStatus] = useState("all")
const { data, hasMore, loadMore } = useDocuments({ documentType: "feedback", filter: ` select(defined($userId) => assignee == $userId, true) && select( $status == "pending" => !defined(status) || status == "pending", $status == "spam" => status == $status, $status == "approved" => status == $status, true ) `, params: { userId, status }, orderings: [{ field: "_createdAt", direction: "desc" }], batchSize: 10, })
return ( <Stack space={2} padding={5}> <StatusSelector status={status} setStatus={setStatus} /> <OnlyMine userId={userId} setUserId={setUserId} /> {data?.map((feedback) => { const isSelected = selectedFeedback?.documentId === feedback.documentId
return ( <Button key={feedback.documentId} onClick={() => setSelectedFeedback(feedback)} mode={isSelected ? "ghost" : "bleed"} tone={isSelected ? "primary" : undefined} > <Suspense fallback={<Spinner />}> <FeedbackPreview {...feedback} /> </Suspense> </Button> ) })} {hasMore && <Button onClick={loadMore} text="Load more" />} </Stack> )}
You should now be able to click the buttons to filter based on user assignment or document status. Our app's really useful now!
The GROQ filter we wrote is a bit gnarly! The select()
function is used here to only filter by a param value if it is not null
.
First it uses defined()
to check if $userId
is not null
. If not, it will only find documents where the assignee
field matches $userId
. If it is null
, the value of the assignee field is not used as part of the filter.
It also applies selective filtering looking at the value of the status
field—first checking for documents without that value (or the value of "pending"), then only showing "spam" or "approved" documents if that's what the current filter matches. Lastly, it just returns everything regardless of the status
field.
We can go further. Let's link your app and the Studio more closely together.
You have 3 uncompleted tasks in this lesson
0 of 3