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

Loading and Displaying Projects

Loading and Displaying Projects

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

WITH QueryClient (chapter 73) and apiClient (chapter 74) in place, the FIRST real component can NOW be built: a project list.

Defining the type

src/types/project.ts
export interface Project {
  '@id': string;
  id: number;
  name: string;
  description: string | null;
  priority: number;
  isRecent: boolean;
  createdAt: string;
}

export interface ProjectCollection {
  'hydra:member': Project[];
  'hydra:totalItems': number;
}

Written MANUALLY, MATCHING project:read (chapter 23) – chapter 80 LATER REPLACES this with AUTOMATICALLY generated types; for THIS chapter, the manual variant is enough to understand the STRUCTURE.

Writing a custom hook

src/hooks/useProjects.ts
import { useQuery } from '@tanstack/react-query';
import { apiClient } from '../api/client';
import type { ProjectCollection } from '../types/project';

export function useProjects() {
  return useQuery({
    queryKey: ['projects'],
    queryFn: async () => {
      const response = await apiClient.get<ProjectCollection>('/projects');

      return response.data;
    },
  });
}

queryKey: ['projects'] UNIQUELY identifies this query in the cache – EVERY component calling useProjects() SHARES the SAME cache entry, instead of EACH sending its OWN request.

Building the component

src/pages/ProjectListPage.tsx
import { useProjects } from '../hooks/useProjects';

function ProjectListPage() {
  const { data, isPending, isError, error } = useProjects();

  if (isPending) {
    return <p>Loading projects...</p>;
  }

  if (isError) {
    return <p>Error: {error.message}</p>;
  }

  return (
    <ul>
      {data['hydra:member'].map((project) => (
        <li key={project.id}>
          {project.name} (priority: {project.priority})
        </li>
      ))}
    </ul>
  );
}

export default ProjectListPage;

isPending/isError/data/error is FULLY handled by TanStack Query – NO custom useState loading state needed, EXACTLY the difference from chapter 8.

Achtung: key={project.id} uses the DATABASE id, NOT the @id IRI – React keys only need to be UNIQUE within the list, the shorter numeric id is COMPLETELY sufficient for that.

Tipp: WITHOUT authentication, this call FAILS (chapter 55: only logged-in users see THEIR projects) – THIS chapter demonstrates the PATTERN, chapters 76-77 add the STILL missing authentication, so the list ACTUALLY shows data.