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

Mercure in React: an EventSource Hook

Mercure in React: an EventSource Hook

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

Chapter 69 tested Mercure subscriptions MANUALLY in the browser – this chapter wraps THAT in a reusable React hook that INFORMS TanStack Query SPECIFICALLY about changes.

Writing the useMercure hook

src/hooks/useMercure.ts
import { useEffect } from 'react';
import { useQueryClient } from '@tanstack/react-query';

const MERCURE_URL = 'https://localhost/.well-known/mercure';

export function useMercure(topic: string, queryKey: unknown[]) {
  const queryClient = useQueryClient();

  useEffect(() => {
    const url = new URL(MERCURE_URL);
    url.searchParams.append('topic', topic);

    const eventSource = new EventSource(url, { withCredentials: true });

    eventSource.onmessage = () => {
      queryClient.invalidateQueries({ queryKey });
    };

    return () => {
      eventSource.close();
    };
  }, [topic, queryClient, queryKey]);
}

INSTEAD of parsing the update message itself and WRITING it into the cache, THIS hook uses invalidateQueries() from chapter 78 – SIMPLER and LESS error-prone, since the SUBSEQUENT refetch ALWAYS fetches the GUARANTEED current, authorized state from the server.

Achtung: withCredentials: true is NECESSARY for EventSource to SEND ALONG the mercureAuthorization cookie from chapter 70 – WITHOUT this option, the browser would NOT automatically attach the cookie on a cross-origin request.

Understanding the cleanup function

return () => { eventSource.close(); } is CRITICAL: WITHOUT this cleanup function, EVERY remount of the component (e.g. navigating away and back) would open an ADDITIONAL, NEVER-closed connection – A classic React memory leak that useEffect's cleanup mechanism exists EXACTLY for THIS case.

Using the hook in the project list

// ProjectListPage.tsx - addition
import { useMercure } from '../hooks/useMercure';

function ProjectListPage() {
  useMercure('https://localhost/api/projects/{id}', ['projects']);

  const { data, isPending, isError, error } = useProjects();
  // ...
}

The WILDCARD topic from chapter 69 ({id} instead of a concrete ID) ensures that CHANGES to ANY project INVALIDATE the ['projects'] cache entry – if ANOTHER user changes ONE project, the list updates for US AUTOMATICALLY, WITHOUT reloading the page.

Testing the complete behavior

  1. Open the project list in TWO browser tabs (or with two different users).
  2. Edit a project in tab 1 (PATCH, EXACTLY as in chapter 53).
  3. Tab 2 updates IMMEDIATELY, WITHOUT anything being clicked THERE.

Tipp: THIS hook is the FRONTEND counterpart to EVERYTHING built in the backend since chapter 67 – A GOOD moment to MENTALLY retrace the chain FROM mercure: true (chapter 68) TO this useEffect ONCE MORE, BEFORE moving on to the FULL CRUD UI in block 10.