Concurrent Operations
Represent overlapping mutations and actions without misleading shared loading state.
One mutation or action composable exposes state for its latest call lifecycle. Decide whether operations should share that state before reusing one instance.
One form, one operation state
const saveSettings = useConvexMutation(api.settings.update)
async function submit() {
if (saveSettings.pending.value) return
await saveSettings.safe(form.value)
}This fits a form where only one submission should be active.
Independent rows need independent state
For a list with per-row actions, create an explicit per-row component:
<script setup lang="ts">
import type { Id } from '~/convex/_generated/dataModel'
import { api } from '#convex/api'
const props = defineProps<{ projectId: Id<'projects'> }>()
const archiveProject = useConvexMutation(api.projects.archive)
</script>
<template>
<button :disabled="archiveProject.pending" @click="archiveProject({ projectId })">
{{ archiveProject.pending ? 'Archiving…' : 'Archive' }}
</button>
</template>Each component instance owns its operation state. One row does not disable every row.
Latest-call state
When calls overlap through one composable, the latest call owns the retained data, error, and status commit. Earlier calls still resolve or reject to their direct callers.
Do not use one pending ref as an exact count of all network operations. Use separate instances or an app-owned queue when the product must display every job.
Disable duplicate writes deliberately
Disable or deduplicate when repeating the operation would be harmful. Backend idempotency remains the reliable protection against retries, double clicks, and network ambiguity.
Reset
reset() clears retained result and error state. It does not cancel a network call or undo a committed mutation.
Identity transition
An identity change masks retained call state and retires pending completions. A remote write may already exist. Treat IDENTITY_CHANGED as an ambiguous local presentation outcome; it does not establish that the server did nothing.