Optimistic updates with rollback
### Describe The Problem To Be Solved
I'm looking for ways to perform [optimistic updates](https://tanstack.com/query/v4/docs/framework/react/guides/optimistic-updates) in Solid, and I think it could be a useful primitive.
---
When a user interacts with a UI element that triggers a server API call (e.g., clicking a like button), there are two ways to handle asynchronicity:
1. The "conservative" way:
- send the request and wait/listen to the response,
- if server success: mutate the frontend state.
- if server failure: display the error message.
2. The "opportunistic" way:
- preemptively mutate the frontend state,
- send the request and wait/listen to the response,
- if server failure: rollback the state and display the error message.
Opportunistic updates make frontends snappier by erasing the client-server communication latency.
### Suggest A Solution
I've recently been using a small utility to provide signals with async updates and rollback functionality.
The short version is below:
<details>
```ts
function createOptimisticSignal<T>(commitFn: (val: T) => PromiseLike<void>, initialVal: T) {
const [committedVal, setCommittedVal] = createSignal<T>(initialVal)
const [tempVal, setTempVal] = createSignal<T>(initialVal)
const commit = async (v: T) => {
setTempVal(v)
try {
await commitFn(v)
setCommittedVal(v)
} catch (e) {
// rollback if update failed
setTempVal(committedVal())
throw e
}
}
createEffect(() => {
// reset the temporary value on commit
setTempVal(committedVal())
})
return {
val: tempVal,
commit,
committedVal,
setCommittedVal,
}
}
```
</details>
in a nutshell, it returns a signal getter and a setter (`val` and `commit`). The commit function must throw or reject if the transaction failed. It also provides means to get and set the committed value manually. A more polished version of this utility would contain error handling and state management, like `createResource()`.
Example usage below:
<details>
```ts
interface Server {
setLiked(b: boolean) => Promise<void> // may throw, if the server fails for some reason
getLiked() => Promise<boolean>
on(event: string, callback: Function) // has events that may affect the 'liked' state
}
const likeButton = createOptimisticSignal<boolean>(server.setLiked, false)
server.on('liked', likeButton.setCommittedVal) // may be triggered if my friend clicks the button…
<button on:click={() => likeButton.commit(!likeButton.val())}>{likeButton.val() ? 'Unlike me!' : 'Like me!'}</button>
```
</details>
关闭于 2026-01-05 3 条评论