TanStackT
TanStack14mo ago
1 reply
urgent-maroon

Input Focus Lost After Mutation with Optimistic Updates

Problem Description
I'm experiencing an input focus issue with React Query mutations. Here's the exact behavior:
1. User types in Input A and triggers a mutation (e.g., updating a module name)
2. User immediately tries to focus Input B
3. Input B gets focused momentarily
4. Input B immediately loses focus
5. User needs to click Input B again to regain focus

The issue occurs with both optimistic updates and regular mutations with invalidateQueries. Here's the interesting part:
- When I comment out the optimistic update code, but keep the API request, everything works fine
- When I add back optimistic updates or use invalidateQueries, the focus issue returns

Relevant Code
Here's a simplified version of my mutation:
export const useUpdateModule = (projectId: number) => {
  const queryClient = useQueryClient();
  const queryKey = [PROJECT_ESTIMATE_QUERY_KEY, { projectId }];

  return useMutation({
    mutationFn: ({ moduleId, data }: UpdateModuleParams) =>
      api.estimates.updateModule(moduleId, data),
    onMutate: async ({ moduleId, data }) => {
      await queryClient.cancelQueries({ queryKey });
      const previousData = queryClient.getQueryData<ProjectEstimateApiResponse>(queryKey);

      queryClient.setQueryData<ProjectEstimateApiResponse>(queryKey, (old) => {
        if (!old) return old;
        return produce(old, (draft) => {
          const module = draft.data.modules.find((m) => m.id === moduleId);
          if (module) {
            module.name = data.name;
            module.is_exportable = data.is_exportable;
          }
        });
      });

      return { previousData };
    },
    onError: (_, __, context) => {
      queryClient.setQueryData(queryKey, context?.previousData);
    }
  });
};

What I've Tried
Using Immer for immutable updates
Memoizing components
Adding stable keys to components
Removing optimistic updates (this works but isn't the desired solution)

and still doesn't work, i think this could be rerenders problem also
Was this page helpful?