swr icon indicating copy to clipboard operation
swr copied to clipboard

Local mutations should work even when not fetching

Open nicholaschiang opened this issue 3 years ago • 3 comments

Bug report

Description / Observed Behavior

I have a UserDialog component that can either create a new user or edit an existing user. When creating a new user, I still want to useSWR to manage the component state like so:

import useSWR from 'swr';

export default function CreateUserDialog({ 
  id, 
  onClosed,
  initialData = (new User().toJSON()), 
}: CreateUserDialogProps): JSX.Element {

  const { data: user, mutate } = useSWR<UserJSON>(id ? `/api/users/${id}` : null, { initialData });

  return (
    <Dialog open onClosed={onClosed} className={styles.dialog}>
      <form className={styles.form}>
        <Inputs value={user as UserJSON} onChange={(updated: UserJSON) => {
          // Locally update the SWR data but don't actually create or fetch the data from the remote
          // until after the user clicks the "create user" button.
          void mutate(updated, false);
        }} />
      </form>
    </Dialog>
  );
}

This doesn't work, however, because I can't seem to locally mutate the SWR data when the hook wasn't given a key (i.e. when I tell the hook not to fetch yet). This makes sense because SWR doesn't have a key yet. But I should still be able to do this when using the bound mutate callback.

Expected Behavior

Ideally, this should be able to work when using the bound mutate callback (OFC it's not going to work when using the global callback because there isn't a key to reference).

nicholaschiang avatar Aug 01 '20 01:08 nicholaschiang

The bound mutate is not a special function, only do this (data, shouldRevalidate) => mutate(key,data, shouldRevalidate) and pass the key of the hook. The source of true of SWR is always the cache, not the internal state of the hook, that is why you can't do that.

I recommend you to create a key for the new user, something like "fake-user" and make your fetcher recognize it and return the new User().toJSON(). Once your component unmount remember to run cache.delete("current-user") so you don't keep the data between usages. Also you may want to disable all revalidations if there is no user.

sergiodxa avatar Aug 01 '20 02:08 sergiodxa

According to docs it should be possible to update local state (stale data) until re-fetch is done... Even with the "bound" mutation function

olelivalife avatar Sep 08 '20 09:09 olelivalife

By local it means the cached data (local is the browser, remote is the API), so you can call mutate(key, newData, false) to mutate the cached data without a revalidation, but you need a key and if for some reason SWR decides to revalidate you will lose the non persisted changes.

sergiodxa avatar Sep 08 '20 15:09 sergiodxa