web-production-saas-starter/next_b2b_starter/lib/hooks/mutations/use-update-profile.ts
2025-12-16 18:54:41 +04:00

73 lines
2.2 KiB
TypeScript

/**
* Update Profile Mutation
*
* Handles profile updates with optimistic UI updates and automatic cache invalidation.
*/
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { memberRepository } from "@/lib/api/api/repositories/member-repository";
import { queryKeys } from "../queries/query-keys";
import type { UpdateProfileRequest, UserProfile } from "@/lib/models/member.model";
export function useUpdateProfile() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (request: UpdateProfileRequest) =>
memberRepository.updateProfile(request),
// Optimistic update: Update UI immediately before API call completes
onMutate: async (newProfile) => {
// Cancel any outgoing refetches
await queryClient.cancelQueries({
queryKey: queryKeys.profile.detail(),
});
// Snapshot the previous value
const previousProfile = queryClient.getQueryData<UserProfile>(
queryKeys.profile.detail()
);
// Optimistically update the cache
if (previousProfile) {
queryClient.setQueryData<UserProfile>(
queryKeys.profile.detail(),
(old) =>
old
? {
...old,
name: newProfile.name ?? old.name,
avatarUrl: newProfile.avatarUrl ?? old.avatarUrl,
}
: old
);
}
// Return context with previous value for rollback
return { previousProfile };
},
// On success, update cache with fresh data from server
onSuccess: (data) => {
queryClient.setQueryData(queryKeys.profile.detail(), data);
},
// On error, rollback to previous value
onError: (error, _variables, context) => {
if (context?.previousProfile) {
queryClient.setQueryData(
queryKeys.profile.detail(),
context.previousProfile
);
}
console.error("[Mutation] Profile update failed:", error);
},
// Always refetch after error or success to ensure consistency
onSettled: () => {
queryClient.invalidateQueries({
queryKey: queryKeys.profile.detail(),
});
},
});
}