0

I have two media hide and unhide, I am using optimistic update approach from react query

It works smoothly, the setup of my react is list of checkbox, 1 list for hidden and 1 list for non-hidden items. I am having problem with race conditions when the checkbox is clicked very fast when unhiding.

This is the optimistic hook for hiding a media:

const useOptimisticHideMedia = (file_type: FileType, profileId: string) => {
    const mutationKey = [REPRESENTATIVES_MUTATION.hide_attached_media_key, file_type, profileId];

    return useMutation({
      mutationKey,

      mutationFn: async (payload: IRepresentativeHideMediaPayload) => {
        return await unSelectProfileMedia(payload.profileId, payload.mediaId);
      },
      onMutate: async (payload: IRepresentativeHideMediaPayload) => {
        let hiddenNode: IRepresentativeSingleProfileHiddenMedia;
        const attachedMediaQueryKey = [
          REPRESENTATIVES_QUERY_STRING.representatives_single_profile_attached_media,
          payload.profileId,
          payload.fileType,
        ];
        const hiddenMediaQueryKey = [
          REPRESENTATIVES_QUERY_STRING.representatives_single_profile_hidden_media,
          payload.profileId,
          payload.fileType,
        ];

        await Promise.all([
          queryClient.cancelQueries(attachedMediaQueryKey),
          // queryClient.cancelQueries(hiddenMediaQueryKey),
        ]);

        const previousAttachedMedia: ISingleRepresentativeAttachedMediaPayloadResponse | undefined =
          queryClient.getQueryData(attachedMediaQueryKey);
        const previousHiddenMedia = queryClient.getQueryData(hiddenMediaQueryKey);

        queryClient.setQueryData(attachedMediaQueryKey, (oldData: any) => {
          const prevData: ISingleRepresentativeAttachedMediaPayloadResponse = oldData;
          return {
            ...prevData,
            data: {
              ...prevData.data,
              medium_attachments_connection: {
                ...prevData.data?.medium_attachments_connection,
                nodes: prevData.data?.medium_attachments_connection?.nodes?.filter((node) => {
                  if (node.id !== payload.mediaId) return true;
                  hiddenNode = {
                    id: node?.medium.id || '',
                    file_name: node?.medium?.file_name || '',
                    file_type: node?.medium?.file_type || '',
                    attachment_url: node?.medium?.attachment_url || '',
                    cropped_attachment_url: node?.medium?.cropped_attachment_url,
                    modified_attachment_url: node?.medium?.modified_attachment_url,
                    modified_metadata: node?.medium?.modified_metadata || '',
                  };
                  return false;
                }),
              },
            },
          };
        });

        queryClient.setQueryData(hiddenMediaQueryKey, (oldData: any) => {
          const prevData: ISingleRepresentativeHiddenMediaPayloadResponse = oldData;

          return {
            ...prevData,
            data: {
              ...prevData.data,
              media_connection: {
                ...prevData.data.media_connection,
                nodes: [hiddenNode, ...(prevData.data.media_connection?.nodes || [])],
              },
            },
          };
        });

        return { previousAttachedMedia, previousHiddenMedia };
      },
      onError: (err, payload, context) => {
        // Rollback optimistic update on error
        const attachedMediaQueryKey = [
          REPRESENTATIVES_QUERY_STRING.representatives_single_profile_attached_media,
          payload.profileId,
          payload.fileType,
        ];
        const hiddenMediaQueryKey = [
          REPRESENTATIVES_QUERY_STRING.representatives_single_profile_hidden_media,
          payload.profileId,
          payload.fileType,
        ];
        queryClient.setQueryData(attachedMediaQueryKey, context?.previousAttachedMedia);
        queryClient.setQueryData(hiddenMediaQueryKey, context?.previousHiddenMedia);
      },
      onSettled: async (data, error, payload) => {
        if (queryClient.isMutating({ mutationKey }) === 1) {
          queryClient.invalidateQueries([
            REPRESENTATIVES_QUERY_STRING.representatives_single_profile_attached_media,
            payload.profileId,
            payload.fileType,
          ]);
          queryClient.invalidateQueries([
            REPRESENTATIVES_QUERY_STRING.representatives_single_profile_hidden_media,
            payload.profileId,
            payload.fileType,
          ]);
        }
      },
    });
  };

This is the Unhide:

const useOptimisticUnHideMedia = (file_type: FileType, profileId: string) => {
    const mutationKey = [REPRESENTATIVES_MUTATION.select_media_key, file_type, profileId];

    return useMutation({
      mutationKey,
      mutationFn: async (payload: IRrepresentativeShowHiddenMediaPayload[]) => {
        return await postRepresentativeShowHiddenMedia(profileId, payload);
      },
      onMutate: async (payload: IRrepresentativeShowHiddenMediaPayload[]) => {
        let hiddenNode: IRepresentativeSingleProfileMedia;
        const attachedMediaQueryKey = [
          REPRESENTATIVES_QUERY_STRING.representatives_single_profile_attached_media,
          profileId,
          file_type,
        ];
        const hiddenMediaQueryKey = [
          REPRESENTATIVES_QUERY_STRING.representatives_single_profile_hidden_media,
          profileId,
          file_type,
        ];

        await Promise.all([
          // queryClient.cancelQueries(attachedMediaQueryKey),
          queryClient.cancelQueries(hiddenMediaQueryKey),
        ]);

        const previousHiddenMedia: ISingleRepresentativeHiddenMediaPayloadResponse | undefined =
          queryClient.getQueryData(hiddenMediaQueryKey);
        const previousAttachedMedia = queryClient.getQueryData(attachedMediaQueryKey);

        queryClient.setQueryData(hiddenMediaQueryKey, (oldData: any) => {
          const prevData: ISingleRepresentativeHiddenMediaPayloadResponse = oldData;

          return {
            ...prevData,
            data: {
              ...prevData.data,
              media_connection: {
                ...prevData?.data.media_connection,
                nodes: prevData?.data?.media_connection?.nodes.filter((node) => {
                  return !payload.some((item) => node.id === item.id);
                }),
              },
            },
          };
        });

        return { previousAttachedMedia, previousHiddenMedia };
      },
      onError: (err, payload, context) => {
        // Rollback optimistic update on error
        const attachedMediaQueryKey = [
          REPRESENTATIVES_QUERY_STRING.representatives_single_profile_attached_media,
          profileId,
          file_type,
        ];
        const hiddenMediaQueryKey = [
          REPRESENTATIVES_QUERY_STRING.representatives_single_profile_hidden_media,
          profileId,
          file_type,
        ];
        queryClient.setQueryData(attachedMediaQueryKey, context?.previousAttachedMedia);
        queryClient.setQueryData(hiddenMediaQueryKey, context?.previousHiddenMedia);
      },
      onSettled: async (data, error, payload) => {
        if (queryClient.isMutating({ mutationKey }) === 1) {
          queryClient.invalidateQueries([
            REPRESENTATIVES_QUERY_STRING.representatives_single_profile_attached_media,
            profileId,
            file_type,
          ]);
          queryClient.invalidateQueries([
            REPRESENTATIVES_QUERY_STRING.representatives_single_profile_hidden_media,
            profileId,
            file_type,
          ]);
        }
      },
    });
  };

I was expecting it to refetch the fresh data from the server. But when I unhide media quickly (e.g. rapidly toggling checkboxes) using the useOptimisticUnHideMedia, it seems like there's a race condition: the invalidation doesn't always trigger a refetch or get the correct updated state. Only when I refresh the page, I get the correct data.

queryClient.invalidateQueries([
            REPRESENTATIVES_QUERY_STRING.representatives_single_profile_attached_media,
            profileId,
            file_type,
          ]);

What can I improve here? Really appreciate the help thanks.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.