Peform
Peform2mo ago

conditionally fetching data with a useQuery

Hello, From my understanding a useMutation is primarily for updating/deleting/creating a record. And a useQuery is primarily used for fetching data from the server. However, when it comes to conditionally fetching data from the server I belive the syntax for a useMutation is much more ideal? I'm not sure if I am missing something, or if in this scenario it would be better to use a useMutation. The issue is, I only want to query the API when the user presses a specific button and once they have input a userId to search. I also want to display a toast (notification) when the request has finished. However, the onSuccess and onError callbacks are depreciated, meaning I have to now use a useEffect. This is greatly overcomplicating everything. I'm not sure if there is a better way of doing this? Any advice would be appreciated.
2 Replies
Peform
Peform2mo ago
export function UserLookup() {
const [userLookupEnabled, setUserLookupEnabled] = useState(false);

const form = useForm<z.infer<typeof lookupUserSchema>>({
resolver: zodResolver(lookupUserSchema),
defaultValues: {
userId: "",
},
});

const { data: userData } = api.userLookup.find.useQuery(
{
userId: form.getValues("userId"),
},
{
enabled: userLookupEnabled && !!form.getValues("userId"),
},
);

// enable the useQuery and fetch some data....
const handleLookupUser = () => {
setUserLookupEnabled(true);
};

return (
<>
<Form {...form}>
<form onSubmit={form.handleSubmit(handleLookupUser)}>
<FormField
control={form.control}
name="userId"
render={({ field }) => (
<FormItem className="grow">
<FormLabel>Prompt</FormLabel>
<FormControl>
<Input
placeholder="Enter your prompt"
maxLength={2048}
className="resize-none"
type="text"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button className="mt-3" type="submit">
Lookup user
</Button>
</form>
</Form>
</>
export function UserLookup() {
const [userLookupEnabled, setUserLookupEnabled] = useState(false);

const form = useForm<z.infer<typeof lookupUserSchema>>({
resolver: zodResolver(lookupUserSchema),
defaultValues: {
userId: "",
},
});

const { data: userData } = api.userLookup.find.useQuery(
{
userId: form.getValues("userId"),
},
{
enabled: userLookupEnabled && !!form.getValues("userId"),
},
);

// enable the useQuery and fetch some data....
const handleLookupUser = () => {
setUserLookupEnabled(true);
};

return (
<>
<Form {...form}>
<form onSubmit={form.handleSubmit(handleLookupUser)}>
<FormField
control={form.control}
name="userId"
render={({ field }) => (
<FormItem className="grow">
<FormLabel>Prompt</FormLabel>
<FormControl>
<Input
placeholder="Enter your prompt"
maxLength={2048}
className="resize-none"
type="text"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button className="mt-3" type="submit">
Lookup user
</Button>
</form>
</Form>
</>
wise_pomelo_81978
I have a few comments here: 1. For the onSuccess and onError deprecation, I just implemented the suggestions here (https://tkdodo.eu/blog/react-query-error-handling#showing-error-notifications) and it isn't too painful. Also dodges some of the edge cases discussed in that blog post. 2. I agree that the "disable query until something is filled in" use-case can feel a bit awkward. Personally, I wouldn't use RHF for this case (although I understand it might be simplified for this post). If it is just a single field, I'd be more tempted to just useState() for the userId. Especially since it looks like from this code that as soon as the form is submitted once, any further changes to the userid trigger another query? Maybe you can stick with this structure and instead of having state for userLookupEnabled, you can have the userId state.
React Query Error Handling
After covering the sunshine cases of data fetching, it's time to look at situations where things don't go as planned and "Something went wrong..."