Skip to content

Optimistic Updates

Optimistically updating the UI without waiting for the server can create a more responsive user experience. Leo Query supports optimistic updates. To implement an optimistic update implement these steps in your effect:

  • Update the Zustand store
  • Execute the async action
  • Revert the Zustand store if the async action fails

Usage

Basic Example

This is an example of an optimistic update with dogs.

typescript
interface DogsState {
  dogs: Query<DogsState, number>;
  increaseDogs: Effect<DogsState, []>;
}

const api = {
  fetchDogs: async () => fetch("/api/dogs"),
  increaseDogs: async () => fetch("/api/dogs/increase", {method: "POST"}),
};

const increaseDogs = (set: (s: Partial<DogsState> => void), get: () => DogsState) => async () => {
  const dogs = get().dogs;
  set({dogs: {...dogs, value: dogs.value + 1}});
  try {
    await api.increaseDogs();
  } catch (error) {
    set({dogs: {...dogs, value: dogs.value - 1}});
  }
};

const useDogStore = create((set, get) => ({
  dogs: query(api.fetchDogs, s => [s.increaseDogs]),
  increaseDogs: effect(increaseDogs(set, set))
}));

const useDogStoreAsync = hook(useDogStore, /*suspense*/ false);

const MyComponent = () => {
  const dogs = useDogStoreAsync(s => s.dogs);
  const increaseDogs = useDogStoreAsync(s => s.increaseDogs);

  //First load, show loading indicator
  if (dogs.value === undefined) {
    return <div>Loading...</div>;
  }

  return (
    <>
      {/* Subsequent loads, ignore isLoading because we optimistically updated the value. */}
      <button onClick={increaseDogs.value}>Increase Dogs</button>
    </>
  );
};