Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Marking a Task as Done

Marking a Task as Done

~12 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026

The MOST COMMON interaction in ANY task manager: ONE click to toggle done – A GOOD example of a mutation WITHOUT its own form.

The toggle hook

src/hooks/useToggleTaskDone.ts
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { apiClient } from '../api/client';
import type { Task } from '../types/task';

interface ToggleTaskInput {
  taskId: number;
  done: boolean;
  projectId: number;
}

export function useToggleTaskDone() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async ({ taskId, done }: ToggleTaskInput) => {
      const response = await apiClient.patch<Task>(
        `/tasks/${taskId}`,
        { done },
        { headers: { 'Content-Type': 'application/merge-patch+json' } },
      );

      return response.data;
    },
    onSuccess: (_data, variables) => {
      queryClient.invalidateQueries({
        queryKey: ['projects', variables.projectId, 'tasks'],
      });
    },
  });
}

The checkbox element

function TaskItem({ task, projectId }: { task: Task; projectId: number }) {
  const toggleDone = useToggleTaskDone();

  return (
    <li>
      <input
        type="checkbox"
        checked={task.done}
        disabled={toggleDone.isPending}
        onChange={(e) =>
          toggleDone.mutate({
            taskId: task.id,
            done: e.target.checked,
            projectId,
          })
        }
      />
      {task.title}
    </li>
  );
}

checked={task.done} makes the checkbox a CONTROLLED React component – the DISPLAYED state ALWAYS comes from the TanStack Query cache, NEVER from a local useState.

Achtung: WITHOUT disabled={toggleDone.isPending}, a FAST double-click could trigger TWO overlapping requests – for a SIMPLE boolean toggle this is HARMLESS (server state ends up AT whichever value was SENT last), for MORE COMPLEX mutations (e.g. incrementing) it could lead to INCONSISTENT behavior.

Why no optimistic update here (yet)

IMMEDIATE visual feedback (BEFORE the server response arrives) would be DESIRABLE PRECISELY for THIS interaction – chapter 91 RETROFITS THIS hook SPECIFICALLY with "optimistic updates", AFTER the BASIC principle has been established here WITHOUT the added complexity.

Tipp: Mercure (chapter 82) could ALSO be enabled on Task (the chapter 72 exercise) – then a toggle in ONE tab would become IMMEDIATELY visible in EVERY other open tab TOO, EXACTLY like with the archived status from chapter 85.