swr
swr copied to clipboard
Bound/global mutate functions do not mutate remote data
Bug report
I'm a longtime SWR user, but I'm still puzzled by the behavior of the three different mutate functions:
- The bound mutate function returned by
useSWR
. - The global mutate function accessible via
useSWRConfig
or withimport { mutate } from "swr"
. - The mutation trigger function returned by
useSWRMutation
.
The docs seem to describe these three functions as three ways to achieve the same goal: mutating data. When I hear "mutating data" in the context of a framework like SWR, I assume that the goal is to update data both remotely and locally, either via an optimistic update (update the local data immediately and revert it if the remote update fails) or a pessimistic update (wait to update the local data until the remote update is confirmed).
However, in practice it seems that only useSWRMutation
performs remote updates. The other two mutation functions only affect local state, which creates an inconsistency with the remote state.
That means that you get some very strange behavior by default: If you use the bound or global mutate function to perform an update, with no additional parameters, then that update will be reverted moments later when revalidation occurs.
Description / Observed Behavior
Let's look at an example (a simplified version of the CodeSandbox below):
const { data, mutate } = useSWR<{ count: number }>("/count");
return (
<div>
<span>Count: {data?.count ?? ""}</span>
<button onClick={(() => {
mutate({ count: data.count + 1 });
}}>
Increment
</button>
</div>
)
This looks like a straightforward use of the bound mutate function. However, if you click the button, the count will go up by 1, then back down to its original value when revalidation occurs, because the remote state is not modified.
Expected Behavior
I'd expect the bound and global mutate functions to trigger the fetcher to modify the remote state, like useSWRMutation
does. The optimistic updates example in the docs seems to show the global mutate function being used in this way, but I've never been able to get it to work; is there something I'm missing?
Alternatively, I'd expect the docs to be very clear that the bound and global mutate functions are only useful when you're making the remote update separately, and that useSWRMutation
is the preferred way of performing mutations.
Repro Steps / Code Example
This CodeSandbox demonstrates how the three mutation functions behave in a very simple scenario: https://codesandbox.io/p/devbox/focused-euler-sp668n?file=%2Fapp%2Fpage.tsx%3A20%2C14
Additional Context
SWR version: 2.2.5