T
TanStack2mo ago
xenial-black

Using RQ with MultipleSelector component in Async

I am using this component and it's set up like this currently...
export default function MultiselectItems() {
const { selectedItems, selectedItemQuantities, onSelectedItemsChange, onItemQuantityChange } = useJacketEditorContext()
const [searchValue, setSearchValue] = useState('')

const { data: items = [] } = useQuery({
queryKey: ['itemsBySearchValue', searchValue],
queryFn: ({ queryKey }) => {
return itemService.getFinishedGoodItems(queryKey[1])
},
})

const itemOptions = useMemo(
() =>
items.map((item) => ({
value: String(item.id),
label: `${item.sku} - ${item.name}`,
disable: false,
key: String(item.id),
})),
[items],
)

return (
<>
{/* Multi-select with tags */}
<div>
<MultiSelect
options={itemOptions}
onSearchSync={(value) => {
setSearchValue(value)
return itemOptions
}}
selectFirstItem={false}
onChange={(opts) => {
onSelectedItemsChange(opts)
}}
placeholder="Search for an item..."
loadingIndicator={
<p className="py-2 text-center text-lg leading-10 text-muted-foreground">loading...</p>
}
emptyIndicator={
<p className="w-full text-center text-lg leading-10 text-muted-foreground">no results found.</p>
}
/>
</div>
// ... (snip) ...
)
export default function MultiselectItems() {
const { selectedItems, selectedItemQuantities, onSelectedItemsChange, onItemQuantityChange } = useJacketEditorContext()
const [searchValue, setSearchValue] = useState('')

const { data: items = [] } = useQuery({
queryKey: ['itemsBySearchValue', searchValue],
queryFn: ({ queryKey }) => {
return itemService.getFinishedGoodItems(queryKey[1])
},
})

const itemOptions = useMemo(
() =>
items.map((item) => ({
value: String(item.id),
label: `${item.sku} - ${item.name}`,
disable: false,
key: String(item.id),
})),
[items],
)

return (
<>
{/* Multi-select with tags */}
<div>
<MultiSelect
options={itemOptions}
onSearchSync={(value) => {
setSearchValue(value)
return itemOptions
}}
selectFirstItem={false}
onChange={(opts) => {
onSelectedItemsChange(opts)
}}
placeholder="Search for an item..."
loadingIndicator={
<p className="py-2 text-center text-lg leading-10 text-muted-foreground">loading...</p>
}
emptyIndicator={
<p className="w-full text-center text-lg leading-10 text-muted-foreground">no results found.</p>
}
/>
</div>
// ... (snip) ...
)
So: 1. User enters a search term in the <MultiSelect>, which changes the SearchTerm state variable 2. The change in SearchTerm triggers RQ, which queries for a list of Items 3. The memo'd itemOptions are calculated 4. The <MultiSelect> updates with the latest options to select from However, by using onSearchSync, there's no loading indicator displayed. I'm dont understand how to use both RQ (for caching) and this component in its async config.
shadcn/ui expansions
shadcn/ui expansions collect lots of useful components which shadcn/ui does not have out of box. All the components are base on shadcn/ui. Just copy and paste. The component is yours.
2 Replies
equal-aqua
equal-aqua2mo ago
Someone will correct me if I'm wrong -- but you probably need to use queryClient.fetchQuery for you to get a promise and then pass it to onSearchAsync https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientfetchquery
xenial-black
xenial-blackOP2mo ago
I think you're exactly right with that recommendation. Here's how the code was modified and appears to be working just fine:
export default function MultiselectItems() {
const { selectedItems, selectedItemQuantities, onSelectedItemsChange, onItemQuantityChange } =
useJacketEditorContext()
const queryClient = useQueryClient()

return (
<>
{/* Multi-select with tags */}
<div>
<MultiSelect
onSearch={async (value) => {
const data = await queryClient.fetchQuery({
queryKey: ['itemsBySearchValue', value],
queryFn: async ({ queryKey }) => {
return await itemService.getFinishedGoodItems(queryKey[1])
},
})

return data.map((item) => ({
value: String(item.id),
label: `${item.sku} - ${item.name}`,
disable: false,
key: String(item.id),
}))
}}
selectFirstItem={false}
onChange={(opts) => {
onSelectedItemsChange(opts)
}}
placeholder="Search for an item..."
loadingIndicator={
<p className="py-2 text-center text-lg leading-10 text-muted-foreground">
<Spinner size="medium" /> loading...
</p>
}
emptyIndicator={
<p className="w-full text-center text-lg leading-10 text-muted-foreground">no results found.</p>
}
/>
</div>
export default function MultiselectItems() {
const { selectedItems, selectedItemQuantities, onSelectedItemsChange, onItemQuantityChange } =
useJacketEditorContext()
const queryClient = useQueryClient()

return (
<>
{/* Multi-select with tags */}
<div>
<MultiSelect
onSearch={async (value) => {
const data = await queryClient.fetchQuery({
queryKey: ['itemsBySearchValue', value],
queryFn: async ({ queryKey }) => {
return await itemService.getFinishedGoodItems(queryKey[1])
},
})

return data.map((item) => ({
value: String(item.id),
label: `${item.sku} - ${item.name}`,
disable: false,
key: String(item.id),
}))
}}
selectFirstItem={false}
onChange={(opts) => {
onSelectedItemsChange(opts)
}}
placeholder="Search for an item..."
loadingIndicator={
<p className="py-2 text-center text-lg leading-10 text-muted-foreground">
<Spinner size="medium" /> loading...
</p>
}
emptyIndicator={
<p className="w-full text-center text-lg leading-10 text-muted-foreground">no results found.</p>
}
/>
</div>
It's quite a bit cleaner, I think. Thanks!

Did you find this page helpful?