Skip to content

mutate optimisticData does not use updated optimistic state on consecutive calls #4100

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
winkcor opened this issue Feb 24, 2025 · 2 comments

Comments

@winkcor
Copy link

winkcor commented Feb 24, 2025

Bug report

Description / Observed Behavior

  const { data, mutate: localMutate } = useSWRImmutable<{ quantity: number }>(
    "key",  () => ({ quantity: 0 }),  {
      fallbackData: { quantity: 0 },
    }
  );

below code work fine

    await mutate( async () => {
        await new Promise((r) => {
          setTimeout(r, 2000);
        });
        console.log("globalLocalState Finish");
        return { quantity: data!.quantity + 1 };
      },
      {
        revalidate: false,
        optimisticData: () => {
          console.log("globalLocalState prev:", data);
          return { quantity: data!.quantity + 1 };
        },
      }
    );

However, in the following code, prev refers to the previous state, not the optimistic state. So when running this twice in a row, prev.quantity remains 0 for both runs, and after the last fetch function resolves, it updates to 1.

As a result, if run multiple times in quick succession, the value only increases once:

    await mutate( "key", async (prev) => {
        await new Promise((r) => {
          setTimeout(r, 2000);
        });
        console.log("globalWithPrev Finish");
        return { quantity: prev!.quantity + 1 };
      },
      {
        revalidate: false,
        optimisticData: (prev) => {
          console.log("globalWithPrev prev", prev);
          return { quantity: prev!.quantity + 1 };
        },
      }
    );

This problem is the same in global mutate and mutate from hook.

Expected Behavior

When optimistic data increases the value, the next update should reflect the increased value, not the initial state.

Repro Steps / Code Example

Click on "with prev" multiple times optimistic update not work till fetch resolve:

CodeSandbox

Additional Context

SWR version: "swr": "^2.2.5", "^2.3.2"
Next.js version: "next": "14", "15"

@arjendevos
Copy link

arjendevos commented Mar 27, 2025

I think it's intended to work like this. Because even with your global state, it doesn't work when you get an error. It will rollback to the state it had where the error occurred as you can see here: https://codesandbox.io/p/devbox/swr-render-error-forked-p5wxxn?workspaceId=ws_HrH9nNH3hTztr5r98WfYRD.

@winkcor
Copy link
Author

winkcor commented Apr 4, 2025

Hi @arjendevos

You're right. In my example, I use optimistic updates for the "Add to Cart" button, and it causes issues when trying to increase the quantity multiple times. While I can revalidate when an error occurs, the problem is that after the first request resolves, the state updates to a lower quantity than expected when clicking multiple times.

If this behavior is intentional, it would be helpful to explicitly mention it in the documentation. It took me a while to figure out because I was also working with several other libraries and initially thought the issue was coming from them, which led to a lot of wasted time.

If this isn't planned to be change, please close this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants