Skip to content

Cleanup of Long-Lived State

Task

Make sure long-lived state and resources do not remain alive after the owning feature is gone.

Solution

Attach cleanup to destruction points and explicitly clear state that can stay reachable for too long.

Code

ts
import { compute, declareController, readonlyAtom } from '@nrgyjs/core';

export const SessionController = declareController(({ scope }) => {
  const cache = scope.atom<Map<string, string>>(new Map(), {
    onDestroy: () => {
      console.log('cache destroyed');
    },
  });

  scope.onDestroy(() => {
    cache.mutate((map) => {
      map.clear();
    });
  });

  const cacheSize = compute(() => cache().size);

  return {
    state: {
      cacheSize: readonlyAtom(cacheSize),
    },
    put: (key: string, value: string) => {
      cache.mutate((map) => {
        map.set(key, value);
      });
    },
  };
});

What to Watch Out For

  • long-lived state needs explicit ownership
  • destruction may need both resource release and value clearing
  • timers, sockets, and subscriptions should be registered in cleanup as well
  • clearing a Map or similar container in place is often better than replacing it with a new value
  • if the atom owns the resource directly, cleanup can also live in the atom onDestroy callback

Common Mistakes

  • assuming large state will disappear automatically
  • forgetting to clear data that remains reachable through shared references
  • releasing only subscriptions while leaving big caches in memory