bhylu.com

Advertisements

React Effect Cleanup

Pair setup and teardown and verify that repeated mounting does not accumulate active listeners.

How it works

A component that subscribes to another system owns the lifetime of that subscription. Cleanup must remove exactly the listener it added. The example uses a small local notice bus so the lifecycle can be observed without a network service. The connected child can be removed and restored. Its effect returns the unsubscribe function. Development StrictMode may subscribe, unsubscribe and subscribe again; the final active listener count should still be one.

Run the code

Use a separate empty lab folder with Node 22.12+ in the 22 series. Save the files below using their displayed paths; browsers may download only the basename, so create src, public or tests folders as listed. The pinned project uses React and React DOM 19.3.1 with Vite 7.3.1. Run npm install, then npm run dev and open the local address Vite prints. React examples do not run in Bhylu’s current playground.

No Apache, MySQL or change to Bhylu’s server configuration is needed. These are local teaching projects, not a publishing package. Keep generated dependencies and lockfiles in your own lab. Review the project setup.

Example

src/main.jsx

import React, { StrictMode, useState, useEffect } from 'react';
import { createRoot } from 'react-dom/client';
import './style.css';

export const listeners = new Set();
function publish() {
  for (const listener of listeners) listener('Desk notice updated');
}
function Connected() {
  const [notice, setNotice] = useState('Waiting for notice');
  useEffect(() => {
    listeners.add(setNotice);
    return () => listeners.delete(setNotice);
  }, []);
  return <output>{notice}</output>;
}
function App() {
  const [shown, setShown] = useState(true);
  return (
    <main>
      <h1>Neighbourhood lending desk</h1>
      <button onClick={() => setShown((value) => !value)}>Toggle connected view</button>
      <button onClick={publish}>Publish local notice</button>
      {shown && <Connected />}
    </main>
  );
}

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

Expected result

Publishing updates the mounted view. Removing it leaves no subscribed listener; remounting creates exactly one.

The Set stores callback identity, and cleanup removes the same callback. Keeping this small bus at module scope represents an external subscription source for the lesson; it is not a suggested replacement for all app state. A real socket, timer or observer needs its own teardown API. Test removal as well as successful delivery, because a view can appear correct while leaked listeners remain active.

For API contracts and version differences, use React’s official documentation.

Advertisements