Back to Query

Mutations

docs/framework/solid/guides/mutations.md

5.90.36.6 KB
Original Source
tsx
import { Switch, Match, Show } from 'solid-js'

function App() {
  const mutation = useMutation(() => ({
    mutationFn: (newTodo) => {
      return axios.post('/todos', newTodo)
    },
  }))

  return (
    <div>
      <Switch>
        <Match when={mutation.isPending}>Adding todo...</Match>
        <Match when={true}>
          <Show when={mutation.isError}>
            <div>An error occurred: {mutation.error.message}</div>
          </Show>

          <Show when={mutation.isSuccess}>
            <div>Todo added!</div>
          </Show>

          <button
            onClick={() => {
              mutation.mutate({ id: new Date(), title: 'Do Laundry' })
            }}
          >
            Create Todo
          </button>
        </Match>
      </Switch>
    </div>
  )
}
tsx
const CreateTodo = () => {
  const mutation = useMutation(() => ({
    mutationFn: (formData) => {
      return fetch('/api', formData)
    },
  }))
  const onSubmit = (event) => {
    event.preventDefault()
    mutation.mutate(new FormData(event.target))
  }

  return <form onSubmit={onSubmit}>...</form>
}
tsx
import { createSignal, Show } from 'solid-js'

const CreateTodo = () => {
  const [title, setTitle] = createSignal('')
  const mutation = useMutation(() => ({ mutationFn: createTodo }))

  const onCreateTodo = (e) => {
    e.preventDefault()
    mutation.mutate({ title: title() })
  }

  return (
    <form onSubmit={onCreateTodo}>
      <Show when={mutation.error}>
        <h5 onClick={() => mutation.reset()}>{mutation.error}</h5>
      </Show>
      <input
        type="text"
        value={title()}
        onInput={(e) => setTitle(e.currentTarget.value)}
      />
      

      <button type="submit">Create Todo</button>
    </form>
  )
}
tsx
useMutation(() => ({
  mutationFn: addTodo,
  onMutate: (variables, context) => {
    // A mutation is about to happen!

    // Optionally return a result containing data to use when for example rolling back
    return { id: 1 }
  },
  onError: (error, variables, onMutateResult, context) => {
    // An error happened!
    console.log(`rolling back optimistic update with id ${onMutateResult.id}`)
  },
  onSuccess: (data, variables, onMutateResult, context) => {
    // Boom baby!
  },
  onSettled: (data, error, variables, onMutateResult, context) => {
    // Error or success... doesn't matter!
  },
}))
tsx
useMutation(() => ({
  mutationFn: addTodo,
  onSuccess: async () => {
    console.log("I'm first!")
  },
  onSettled: async () => {
    console.log("I'm second!")
  },
}))
tsx
useMutation(() => ({
  mutationFn: addTodo,
  onSuccess: (data, variables, onMutateResult, context) => {
    // I will fire first
  },
  onError: (error, variables, onMutateResult, context) => {
    // I will fire first
  },
  onSettled: (data, error, variables, onMutateResult, context) => {
    // I will fire first
  },
}))

mutate(todo, {
  onSuccess: (data, variables, onMutateResult, context) => {
    // I will fire second!
  },
  onError: (error, variables, onMutateResult, context) => {
    // I will fire second!
  },
  onSettled: (data, error, variables, onMutateResult, context) => {
    // I will fire second!
  },
})
tsx
useMutation(() => ({
  mutationFn: addTodo,
  onSuccess: (data, variables, onMutateResult, context) => {
    // Will be called 3 times
  },
}))

const todos = ['Todo 1', 'Todo 2', 'Todo 3']
todos.forEach((todo) => {
  mutate(todo, {
    onSuccess: (data, variables, onMutateResult, context) => {
      // Will execute only once, for the last mutation (Todo 3),
      // regardless which mutation resolves first
    },
  })
})
tsx
const mutation = useMutation(() => ({ mutationFn: addTodo }))

try {
  const todo = await mutation.mutateAsync(todo)
  console.log(todo)
} catch (error) {
  console.error(error)
} finally {
  console.log('done')
}
tsx
const mutation = useMutation(() => ({
  mutationFn: addTodo,
  retry: 3,
}))
tsx
const queryClient = new QueryClient()

// Define the "addTodo" mutation
queryClient.setMutationDefaults(['addTodo'], {
  mutationFn: addTodo,
  onMutate: async (variables, context) => {
    // Cancel current queries for the todos list
    await context.client.cancelQueries({ queryKey: ['todos'] })

    // Create optimistic todo
    const optimisticTodo = { id: uuid(), title: variables.title }

    // Add optimistic todo to todos list
    context.client.setQueryData(['todos'], (old) => [...old, optimisticTodo])

    // Return a result with the optimistic todo
    return { optimisticTodo }
  },
  onSuccess: (result, variables, onMutateResult, context) => {
    // Replace optimistic todo in the todos list with the result
    context.client.setQueryData(['todos'], (old) =>
      old.map((todo) =>
        todo.id === onMutateResult.optimisticTodo.id ? result : todo,
      ),
    )
  },
  onError: (error, variables, onMutateResult, context) => {
    // Remove optimistic todo from the todos list
    context.client.setQueryData(['todos'], (old) =>
      old.filter((todo) => todo.id !== onMutateResult.optimisticTodo.id),
    )
  },
  retry: 3,
})

// Start mutation in some component:
const mutation = useMutation(() => ({ mutationKey: ['addTodo'] }))
mutation.mutate({ title: 'title' })

// If the mutation has been paused because the device is for example offline,
// Then the paused mutation can be dehydrated when the application quits:
const state = dehydrate(queryClient)

// The mutation can then be hydrated again when the application is started:
hydrate(queryClient, state)

// Resume the paused mutations:
queryClient.resumePausedMutations()

Persisting Offline mutations

If you persist offline mutations with the persistQueryClient plugin, mutations cannot be resumed when the page is reloaded unless you provide a default mutation function.

tsx
const mutation = useMutation(() => ({
  mutationFn: addTodo,
  scope: {
    id: 'todo',
  },
}))

Further reading

For more information about mutations, have a look at TkDodo's article on Mastering Mutations in TanStack Query.