controlled inputs
Hi, I'm fairly new to RQ but adopting it quickly. All seems to be going well but I'm a bit stuck in what's the best practice with controlled inputs. Before RQ I've used local state for an input, initializing it from server data and then using onChange events to post to server and then update the local state. Anyone have any articles, blogs, etc that points the the approaches for React controlled inputs when using RQ? Thanks!
3 Replies
fascinating-indigo•3y ago
React Query and Forms
Forms tend to blur the line between server and client state, so let's see how that plays together with React Query.
robust-apricotOP•3y ago
@TkDodo 🔮, thanks for the link! Read through it and it's close to what I want to do but I think I may be missing something. In my UI I'm trying to accomplish "real-time" updates (without requiring the user to push a "submit changes" button). That is, they should be able to edit the email field and then either press Enter or blur from it and that should submit the change.
- If I make the input controlled by the RQ query cached value, it will only take one character at a time and then put the cursor at the end of the input field (my mutation is returning the inputted value on success).
- If I use a local useState for the input, then I have to do both an RQ mutation and set the state value. That works for a moment but then at re-render the query value is older than the local state value and it reverts. If I wait long enough then the cache reflects the server and I eventually get the correct value. Note, in my onSuccess I'm doing both setQueryData and invalidateQueries.
I feel I'm close, just missing something. Here's the pertinent code (minus the key and blur handling): export const useUpdateDemoConfig = () => { const queryClient = useQueryClient() return useMutation({ mutationFn: async (mutatedOrgData) => mutateOrgRecord(mutatedOrgData), onSuccess: (data, variables) => { queryClient.setQueryData(['demoConfig', variables.id], data) queryClient.invalidateQueries({ queryKey: ["demoConfig", variables.id] }); }, onError: (error, variables, context) => { queryClient.invalidateQueries({ queryKey: ["demoConfig"] }); }, }) }; const updateDemoConfig = useUpdateDemoConfig(); function handleChangeSpocEmail(value) { if (value !== demoConfig.spoc_email) { updateDemoConfig.mutate( { ...demoConfig, spoc_email: value, }, { onSuccess: (data) => setSpocEmail(data.spoc_email) } ); } } Thanks for your help!
- If I use a local useState for the input, then I have to do both an RQ mutation and set the state value. That works for a moment but then at re-render the query value is older than the local state value and it reverts. If I wait long enough then the cache reflects the server and I eventually get the correct value. Note, in my onSuccess I'm doing both setQueryData and invalidateQueries.
I feel I'm close, just missing something. Here's the pertinent code (minus the key and blur handling): export const useUpdateDemoConfig = () => { const queryClient = useQueryClient() return useMutation({ mutationFn: async (mutatedOrgData) => mutateOrgRecord(mutatedOrgData), onSuccess: (data, variables) => { queryClient.setQueryData(['demoConfig', variables.id], data) queryClient.invalidateQueries({ queryKey: ["demoConfig", variables.id] }); }, onError: (error, variables, context) => { queryClient.invalidateQueries({ queryKey: ["demoConfig"] }); }, }) }; const updateDemoConfig = useUpdateDemoConfig(); function handleChangeSpocEmail(value) { if (value !== demoConfig.spoc_email) { updateDemoConfig.mutate( { ...demoConfig, spoc_email: value, }, { onSuccess: (data) => setSpocEmail(data.spoc_email) } ); } } Thanks for your help!
robust-apricotOP•3y ago
@TkDodo 🔮 ...never mind! It's working; I forgot that I had caching enabled at the server LOL! I ended up using the format given in at https://tanstack.com/query/latest/docs/react/guides/optimistic-updates#updating-a-single-todo
So to summarize, for controlled inputs, I'm using local state (useState) to allow for editing and then mutation
- updates cache manually
- triggers cache to refetch
- returns new data for temporary population of the local state.
That keeps the local state using the new data until the cache updates and the overlap is seamless. Niiiiice!
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 ...