---
name: use_mutation
kind: component
lang: ts
domain: core
version: "1.0.0"
purity: impure
signature: "useMutation<TData, TVariables>(opts: UseMutationOptions<TData, TVariables>): MutationState<TData, TVariables>"
description: "Hook POST/PUT/DELETE con optimistic updates, invalidacion de cache, retry y callbacks completos. No ejecuta al montar — expone mutate y mutate_async."
tags: [mutation, hook, post, put, delete, http, optimistic, cache, core, component]
uses_functions: [http_cache_ts_core]
uses_types: [MutationState_ts_core]
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [react]
params:
  - name: mutation_fn
    desc: "Funcion que ejecuta la mutacion, recibe variables y retorna Promise<TData>"
  - name: on_mutate
    desc: "Callback antes de la mutacion para optimistic updates. Retorna contexto de rollback."
  - name: on_success
    desc: "Callback al exito con data, variables y context"
  - name: on_error
    desc: "Callback al error con error, variables y context para rollback"
  - name: on_settled
    desc: "Callback siempre (exito o error)"
  - name: invalidate_cache
    desc: "Prefijos de cache a invalidar en exito (ej: 'users' invalida users, users:list, etc.)"
  - name: retry
    desc: "Numero de reintentos ante error (default 0)"
  - name: retry_delay
    desc: "Delay en ms entre reintentos (default 1000)"
output: "MutationState<TData, TVariables> con mutate, mutate_async, data, error, loading y reset"
tested: false
tests: []
test_file_path: ""
file_path: "frontend/functions/core/use_mutation.tsx"
props:
  - name: mutation_fn
    type: "(variables: TVariables) => Promise<TData>"
    required: true
    description: "Funcion que ejecuta la mutacion"
  - name: on_mutate
    type: "(variables: TVariables) => Promise<unknown> | unknown"
    required: false
    description: "Optimistic update antes de la mutacion"
  - name: on_success
    type: "(data: TData, variables: TVariables, context: unknown) => void"
    required: false
    description: "Callback al exito"
  - name: on_error
    type: "(error: Error, variables: TVariables, context: unknown) => void"
    required: false
    description: "Callback al error (para rollback de optimistic updates)"
  - name: invalidate_cache
    type: "string[]"
    required: false
    description: "Prefijos de cache a invalidar en exito"
  - name: retry
    type: "number"
    required: false
    description: "Reintentos ante error (default 0)"
  - name: retry_delay
    type: "number"
    required: false
    description: "Ms entre reintentos (default 1000)"
emits: [on_success, on_error, on_settled]
has_state: true
framework: react
variant: [default]
---

## Ejemplo

```tsx
import { useMutation } from '@/functions/core/use_mutation'
import { apiClient } from '@/functions/core/api_client'

const api = apiClient({ base_url: 'http://localhost:8484/api' })

function DeleteButton({ userId }: { userId: string }) {
  const { mutate: deleteUser, loading } = useMutation<void, string>({
    mutation_fn: (id) => api.del(`/users/${id}`),
    invalidate_cache: ['users'],
    on_success: () => console.log('Eliminado'),
  })

  return (
    <button disabled={loading} onClick={() => deleteUser(userId)}>
      {loading ? 'Eliminando...' : 'Eliminar'}
    </button>
  )
}
```

## Optimistic update

```tsx
const { mutate } = useMutation<User, Partial<User>>({
  mutation_fn: (data) => api.post('/users', data),
  on_mutate: (newUser) => {
    // Guardar snapshot para rollback
    const prev = cacheGet<User[]>('users')
    cacheSet('users', [...(prev ?? []), { ...newUser, id: 'temp' }])
    return { prev }
  },
  on_error: (_err, _vars, context) => {
    // Rollback
    const ctx = context as { prev: User[] }
    cacheSet('users', ctx.prev)
  },
})
```

## Notas

- `mutate` es fire-and-forget (no lanza, silencia el error)
- `mutate_async` retorna Promise (puede rechazar — usar try/catch)
- `invalidate_cache` usa `cacheInvalidatePrefix` — invalida prefijos jerarquicos
