Problems with useQuery.onSuccess()

Hi there, I'm doing a query on my trpc router and when it succeds I want to put that data on a useState that expects a ReactElement based upon the data from the query. The data from the query response is an array with objects like {id: string, title: string}, when I get them I run a data.forEach() on the returning array but when I have more than one object on the array only one is displayed on the client. Here is a snippet of what I'm doing, perhaps someone can see what's wrong with my code
const CategoriesIndex = ({ session }: Props) => {
const [links, setLinks] = useState<ReactElement<typeof CategoryLink>[]>([]);
api.menu.getSections.useQuery(undefined, {
enabled: session.user !== undefined,
onSuccess(data) {
console.log(data);
data.forEach((categorie) => {
setLinks([
...links,
<CategoryLink
title={categorie.title}
id={categorie.id}
key={categorie.id}
items={0}
/>,
]);
});
},
});
return (
<div className="col-start-1 col-end-7 row-start-3 grid w-full justify-items-center gap-4">
<Button
style="rounded-lg bg-gray-600 h-10 col-start-1 col-end-7 w-full"
name="add-category"
label="Añadir sección"
cb={mutation.mutate}
/>
{links}
</div>
);
const CategoriesIndex = ({ session }: Props) => {
const [links, setLinks] = useState<ReactElement<typeof CategoryLink>[]>([]);
api.menu.getSections.useQuery(undefined, {
enabled: session.user !== undefined,
onSuccess(data) {
console.log(data);
data.forEach((categorie) => {
setLinks([
...links,
<CategoryLink
title={categorie.title}
id={categorie.id}
key={categorie.id}
items={0}
/>,
]);
});
},
});
return (
<div className="col-start-1 col-end-7 row-start-3 grid w-full justify-items-center gap-4">
<Button
style="rounded-lg bg-gray-600 h-10 col-start-1 col-end-7 w-full"
name="add-category"
label="Añadir sección"
cb={mutation.mutate}
/>
{links}
</div>
);
12 Replies
Brendonovich
Brendonovich17mo ago
It’s because you’re doing …links. Calling setLinks doesn’t immediately update the value, so it’s using the old value for each call of forEach. The proper way to do what you’re attempting is to get rid of the useState and onSuccess, assign the query to a variable, and map over the query’s data inside the JSX you’re returning
f2bear
f2bear17mo ago
I'd tried by just using an array like const links: ReactElement<typeof CategoryLink>[] = [] and then just pushing the data but that does not display nothing on the dom for some reason
pandeyman
pandeyman17mo ago
copying your server state to the client on onSuccess is not really a good idea, you should directly map over the data that query returns, you can check for loading state and show a loader while its loading for example.
f2bear
f2bear17mo ago
oh so the onSuccess copy the state from server to client? I believed it was just the callback on the client from where the query was finished
Brendonovich
Brendonovich17mo ago
You’ll wanna do something more like this
const CategoriesIndex = ({ session }: Props) => {
const categories = api.menu.getSections.useQuery(undefined, {
enabled: session.user !== undefined
});

return (
<div className="col-start-1 col-end-7 row-start-3 grid w-full justify-items-center gap-4">
<Button
style="rounded-lg bg-gray-600 h-10 col-start-1 col-end-7 w-full"
name="add-category"
label="Añadir sección"
cb={mutation.mutate}
/>
{categories.data?.map((categorie) => (
<CategoryLink title={categorie.title} id={categorie.id} key={categorie.id} items={0} />
))}
</div>
);
};
const CategoriesIndex = ({ session }: Props) => {
const categories = api.menu.getSections.useQuery(undefined, {
enabled: session.user !== undefined
});

return (
<div className="col-start-1 col-end-7 row-start-3 grid w-full justify-items-center gap-4">
<Button
style="rounded-lg bg-gray-600 h-10 col-start-1 col-end-7 w-full"
name="add-category"
label="Añadir sección"
cb={mutation.mutate}
/>
{categories.data?.map((categorie) => (
<CategoryLink title={categorie.title} id={categorie.id} key={categorie.id} items={0} />
))}
</div>
);
};
pandeyman
pandeyman17mo ago
THIS, plus you can go over this blog post for more details on why copying server state is not a good idea https://tkdodo.eu/blog/practical-react-query
Practical React Query
Let me share with you the experiences I have made lately with React Query. Fetching data in React has never been this delightful...
barry
barry17mo ago
Also don't save JSX into state.
f2bear
f2bear17mo ago
hey this did work, thanks! I guessed that onSuccess was the correct place to do this for some reason hahah cool I'll check that out! why is that? some perf issue or it's just insecure af?
barry
barry17mo ago
Just not how it's done in react world, state contains info, not jsx.
f2bear
f2bear17mo ago
all right, would take that in mind next time! and what should I do if I had a mutation that adds a new record on the same component and I want to add it to the dom as well? should I need to run the query again after the mutation?
barry
barry17mo ago
Optimistic Updates | TanStack Query Docs
When you optimistically update your state before performing a mutation, there is a chance that the mutation will fail. In most of these failure cases, you can just trigger a refetch for your optimistic queries to revert them to their true server state. In some circumstances though, refetching may not work correctly and the mutation error could ...
f2bear
f2bear17mo ago
cool, I'll look at that as well many thanks guys you save me