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 bears.

typescript
interface BearsState {
  bears: Query<BearsState, number>;
  increaseBears: Effect<BearsState, []>;
}

const api = {
  fetchBears: async () => fetch("/api/bears"),
  increaseBears: async () => fetch("/api/bears/increase", {method: "POST"}),
};

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

const useBearStore = create((set, get) => ({
  bears: query(api.fetchBears, s => [s.increaseBears]),
  increaseBears: effect(increaseBears(set, set))
}));

const useBearStoreAsync = hook(useBearStore, /*suspense*/ false);

const MyComponent = () => {
  const bears = useBearStoreAsync(s => s.bears);
  const increaseBears = useBearStoreAsync(s => s.increaseBears);

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

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