Rooks
HooksExperimental Hooks

useDisposable

Manages synchronous disposable resources using the TC39 Explicit Resource Management proposal.

About

Bridges TC39 Explicit Resource Management (Symbol.dispose) with React lifecycles. It creates a disposable resource synchronously so the resource is available on the first render, and disposes it when the component unmounts or when the dependency array changes.

This hook requires Symbol.dispose support at runtime. Browsers such as Safari currently need a polyfill. Load one like core-js/proposals/explicit-resource-management before using this hook.

Examples

Basic usage

import { useDisposable } from "rooks/experimental";

class ManagedWebSocket {
  socket = new WebSocket("wss://example.com");

  [Symbol.dispose]() {
    this.socket.close();
  }
}

function ChatRoom() {
  const socket = useDisposable(() => new ManagedWebSocket(), []);

  return <button onClick={() => socket.socket.send("hello")}>Send</button>;
}

Recreate a disposable resource when dependencies change

import { useState } from "react";
import { useDisposable } from "rooks/experimental";

class UserScopedStore {
  constructor(public userId: string) {}

  [Symbol.dispose]() {
    // clean up user-specific resources
  }
}

export default function UserStorePanel() {
  const [userId, setUserId] = useState("alice");
  const store = useDisposable(() => new UserScopedStore(userId), [userId]);

  return (
    <div>
      <button onClick={() => setUserId("bob")}>Switch user</button>
      <p>Active store: {store.userId}</p>
    </div>
  );
}

Parameters

ArgumentTypeDescriptionDefault value
factory() => TFunction that creates the disposable resourcerequired
depsDependencyListDependency array controlling when the resource is replaced[]

Return value

Return valueTypeDescription
resourceTThe disposable resource, available synchronously on every render

Notes

  • Import from rooks/experimental, not rooks.
  • rooks does not polyfill Symbol.dispose for you.
  • If the returned resource does not implement [Symbol.dispose](), the hook throws a descriptive runtime error.

Behavior and lifecycle

The factory runs synchronously during render so the resource is immediately available. A dependency change disposes the old resource during effect cleanup, creates its replacement in the next effect, and forces a render; unmount invokes [Symbol.dispose]() once for the current resource. Development Strict Mode can create and dispose more than once, so the factory and disposer must tolerate replay.

Compatibility and accessibility

Because creation happens during render, the factory also runs during SSR and must not access browser globals unless guarded. Missing Symbol.dispose, a resource without the required method, a factory failure, or a disposal failure throws to the nearest error boundary. Load the polyfill before rendering in unsupported runtimes.

On this page