Blog>
Snippets

Implementing Offline Mutations with useMutation Hook

Illustrate using the useMutation hook for submitting data (create/update/delete operations) while offline and synchronizing these mutations with the backend upon re-establishing a connection.
import { useMutation, useQueryClient } from 'react-query';
import { updateExerciseStatus } from './api';
import AsyncStorage from '@react-native-async-storage/async-storage';

const useOfflineMutation = (key, mutationFn) => {
  const queryClient = useQueryClient();

  const { mutate } = useMutation(mutationFn, {
    onMutate: async variables => {
      await queryClient.cancelQueries(key);
      const previousValue = queryClient.getQueryData(key);
      // Optionally update optimistic UI here
      return { previousValue };
    },
    onError: (err, variables, context) => {
      // Optionally rollback optimistic UI here
      if (context?.previousValue) {
        queryClient.setQueryData(key, context.previousValue);
      }
    },
    onSuccess: (data, variables, context) => {
      // Update UI with successful response
      queryClient.invalidateQueries(key);
    },
    onSettled: (data, error, variables, context) => {
      // Handle mutation settled state
    }
  });

  return mutate;
};
Defines a custom hook that wraps useMutation for offline support. This hook handles optimistic updates, rollbacks on errors, and automatic UI updates on success. It leverages AsyncStorage for storing mutations when offline.
import { NetInfo } from '@react-native-community/netinfo';

const submitMutation = async (payload) => {
  const isConnected = await NetInfo.fetch().then(state => state.isConnected);
  if (!isConnected) {
    // Store mutation payload in AsyncStorage for later execution
    const currentMutations = await AsyncStorage.getItem('offlineMutations') || '[]';
    const mutations = JSON.parse(currentMutations);
    mutations.push(payload);
    await AsyncStorage.setItem('offlineMutations', JSON.stringify(mutations));
  } else {
    // Directly perform mutation as the device is online
    useOfflineMutation('key', updateExerciseStatus)(payload);
  }
};
Defines the logic for handling mutations when the device is offline. It checks the device's connectivity status and stores the mutation in AsyncStorage if offline. Otherwise, it performs the mutation immediately.
const syncOfflineMutations = async () => {
  const mutations = JSON.parse(await AsyncStorage.getItem('offlineMutations') || '[]');
  mutations.forEach(mutation => {
    useOfflineMutation('key', updateExerciseStatus)(mutation);
    // Remove each processed mutation from AsyncStorage here
  });
};
Implements the function to synchronize mutations stored in AsyncStorage when the device goes back online. It iterates over each stored mutation, performs them, and should remove them from storage upon successful completion.