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

Die Task-Liste eines Projekts

Die Task-Liste eines Projekts

~14 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026

Der verschachtelte Endpunkt aus Kapitel 39 (/api/projects/{id}/tasks) wird JETZT auf der Detailseite (Kapitel 81) genutzt, um NUR die Tasks EINES Projekts zu zeigen.

Den Task-Typ und Hook

src/types/task.ts
export interface Task {
  '@id': string;
  id: number;
  title: string;
  done: boolean;
}

export interface TaskCollection {
  'hydra:member': Task[];
  'hydra:totalItems': number;
}
src/hooks/useProjectTasks.ts
import { useQuery } from '@tanstack/react-query';
import { apiClient } from '../api/client';
import type { TaskCollection } from '../types/task';

export function useProjectTasks(projectId: number) {
  return useQuery({
    queryKey: ['projects', projectId, 'tasks'],
    queryFn: async () => {
      const response = await apiClient.get<TaskCollection>(
        `/projects/${projectId}/tasks`,
      );

      return response.data;
    },
  });
}

queryKey: ['projects', projectId, 'tasks'] statt einfach ['tasks'] – ZUSAMMENGESETZTE Query-Keys sind bei PARAMETRISIERTEN Queries GÄNGIGE Praxis: JEDES Projekt bekommt SEINEN EIGENEN Cache-Eintrag, statt sich EINEN gemeinsamen zu teilen.

Die Detailseite erweitern

src/pages/ProjectDetailPage.tsx
import { useParams } from 'react-router-dom';
import { useProjectTasks } from '../hooks/useProjectTasks';

function ProjectDetailPage() {
  const { id } = useParams<{ id: string }>();
  const projectId = Number(id);
  const { data, isPending } = useProjectTasks(projectId);

  if (isPending) {
    return <p>Lade Tasks...</p>;
  }

  return (
    <ul>
      {data['hydra:member'].map((task) => (
        <li key={task.id}>
          {task.done ? '☑' : '☐'} {task.title}
        </li>
      ))}
    </ul>
  );
}

export default ProjectDetailPage;

Number(id) ist NÖTIG, da useParams IMMER string zurückgibt (URL-Parameter sind TEXT) – die projectId-Prop VON useProjectTasks erwartet aber number, PASSEND zur Backend-id.

Achtung: OHNE Guard für Number.isNaN(projectId) würde eine UNGÜLTIGE URL wie /projects/abc einen Request mit NaN in der Pfad auslösen – für ein PRODUKTIONSREIFES UI wäre eine FRÜHE Prüfung mit Weiterleitung auf eine 404-Seite die SAUBERERE Lösung.

Tipp: invalidateQueries({ queryKey: ['projects', projectId, 'tasks'] }) in EINEM künftigen useCreateTask-Hook (Kapitel 87) muss den GLEICHEN zusammengesetzten Key treffen – EIN Tippfehler HIER würde die Liste NICHT aktualisieren, OHNE dass TypeScript das ERKENNEN könnte (Query-Keys sind ZUR Laufzeit einfache Arrays).