{"id":13523365,"url":"https://github.com/kristoferlund/ic-use-actor","last_synced_at":"2025-12-26T18:10:34.098Z","repository":{"id":215696001,"uuid":"739417775","full_name":"kristoferlund/ic-use-actor","owner":"kristoferlund","description":"React Hook and context provider to make interacting with Internet Computer canisters more fun!","archived":false,"fork":false,"pushed_at":"2024-10-16T20:52:58.000Z","size":139,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-24T07:07:07.445Z","etag":null,"topics":["dfinity","hook","internet-computer","react"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/ic-use-actor","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kristoferlund.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-05T14:15:58.000Z","updated_at":"2024-10-20T06:23:15.000Z","dependencies_parsed_at":"2024-01-18T12:29:43.820Z","dependency_job_id":"4746c65a-65a4-4eb9-9c89-b9206e27ee8e","html_url":"https://github.com/kristoferlund/ic-use-actor","commit_stats":null,"previous_names":["kristoferlund/ic-use-actor"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kristoferlund%2Fic-use-actor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kristoferlund%2Fic-use-actor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kristoferlund%2Fic-use-actor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kristoferlund%2Fic-use-actor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kristoferlund","download_url":"https://codeload.github.com/kristoferlund/ic-use-actor/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248166927,"owners_count":21058480,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dfinity","hook","internet-computer","react"],"created_at":"2024-08-01T06:00:59.407Z","updated_at":"2025-12-26T18:10:34.089Z","avatar_url":"https://github.com/kristoferlund.png","language":"TypeScript","funding_links":[],"categories":["Client Libraries (Agents)"],"sub_categories":["JavaScript/TypeScript"],"readme":"# ic-use-actor\n\nA React hook library for interacting with Internet Computer (IC) canisters. `ic-use-actor` provides a simple, type-safe way to interact with IC actors using XState stores for state management.\n\n[![version][version-image]][npm-link]\n[![downloads][dl-image]][npm-link]\n\n## Features\n\n- **Simple API**: Just one function call to create a typed hook for your canister\n- **No Provider Hell**: No need for React Context or Provider components\n- **Type Safety**: Full TypeScript support with canister service definitions\n- **Request/Response Interceptors**: Process requests and responses with customizable callbacks\n- **Global State Management**: Powered by XState stores for predictable state management\n- **Multiple Canisters**: Easy to work with multiple canisters without nesting providers\n\n## Table of Contents\n\n- [ic-use-actor](#ic-use-actor)\n  - [Features](#features)\n  - [Table of Contents](#table-of-contents)\n  - [Installation](#installation)\n  - [Quick Start](#quick-start)\n  - [Usage](#usage)\n    - [Basic Setup](#basic-setup)\n    - [Using in Components](#using-in-components)\n    - [Multiple Canisters](#multiple-canisters)\n  - [Advanced Usage](#advanced-usage)\n    - [Interceptors](#interceptors)\n    - [Error Handling](#error-handling)\n    - [Custom HTTP Agent Options](#custom-http-agent-options)\n  - [API Reference](#api-reference)\n    - [createActorHook](#createactorhook)\n    - [Hook Return Value](#hook-return-value)\n    - [Global Helpers](#global-helpers)\n  - [Migration from v0.1.x](#migration-from-v01x)\n  - [Examples](#examples)\n  - [Author](#author)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n## Installation\n\n```bash\nnpm install ic-use-actor @icp-sdk/core\n```\n\nor\n\n```bash\nyarn add ic-use-actor @icp-sdk/core\n```\n\nor\n\n```bash\npnpm add ic-use-actor @icp-sdk/core \n```\n\n## Quick Start\n\n```tsx\n// 1. Create your actor hook\nimport { createActorHook } from \"ic-use-actor\";\nimport { canisterId, idlFactory } from \"./declarations/my_canister\";\nimport { _SERVICE } from \"./declarations/my_canister/my_canister.did\";\n\nexport const useMyCanister = createActorHook\u003c_SERVICE\u003e({\n  canisterId,\n  idlFactory,\n});\n\n// 2. Use it in your components\nfunction MyComponent() {\n  const { actor: myCanister, authenticate, isAuthenticated, status, isInitializing, isSuccess, isError, error } = useMyCanister();\n  const { identity } = useInternetIdentity(); // or any identity provider\n\n  // Authenticate when identity is available (keeps initialization separate from authentication)\n  useEffect(() =\u003e {\n    if (identity) {\n      void authenticate(identity);\n    }\n  }, [identity, authenticate]);\n\n  const handleClick = async () =\u003e {\n    if (!myCanister) return;\n    const result = await myCanister.myMethod();\n    console.log(result);\n  };\n\n  if (error) return \u003cdiv\u003eError: {error.message}\u003c/div\u003e;\n  if (isInitializing) return \u003cdiv\u003eLoading...\u003c/div\u003e;\n  if (!isAuthenticated) return \u003cdiv\u003ePlease sign in\u003c/div\u003e;\n\n  return \u003cbutton onClick={handleClick}\u003eCall Canister\u003c/button\u003e;\n}\n\n// 3. That's it!\nfunction App() {\n  return \u003cMyComponent /\u003e;\n}\n```\n\n## Usage\n\n### Basic Setup\n\nCreate a hook for your canister by calling `createActorHook` with your canister's configuration:\n\n```tsx\n// actors.ts\nimport { createActorHook } from \"ic-use-actor\";\nimport { canisterId, idlFactory } from \"./declarations/backend\";\nimport { _SERVICE } from \"./declarations/backend/backend.did\";\n\nexport const useBackendActor = createActorHook\u003c_SERVICE\u003e({\n  canisterId,\n  idlFactory,\n});\n```\n\n### Using in Components\n\nThe hook returns an object with the actor instance and several utility functions:\n\n```tsx\nfunction MyComponent() {\n  const {\n    actor,           // The actor instance (initialized with anonymous agent by default)\n    authenticate,    // Function to authenticate the actor with an identity\n    setInterceptors, // Function to set up interceptors\n    isAuthenticated, // Boolean indicating if actor is authenticated\n    status,          // 'initializing' | 'success' | 'error'\n    isInitializing,  // status === 'initializing'\n    isSuccess,       // status === 'success'\n    isError,         // status === 'error'\n    error,           // Any error that occurred during initialization\n    reset,           // Function to reset the actor state\n    clearError       // Function to clear error state\n  } = useBackendActor();\n\n  const { identity } = useInternetIdentity();\n\n  // Authenticate when identity is available\n  useEffect(() =\u003e {\n    if (identity) {\n      void authenticate(identity);\n    }\n  }, [identity, authenticate]);\n\n  // Use the actor (works with anonymous or authenticated)\n  const fetchData = async () =\u003e {\n    if (!actor) return;\n    try {\n      const data = await actor.getData();\n      console.log(data);\n    } catch (err) {\n      console.error(\"Failed to fetch data:\", err);\n    }\n  };\n\n  return (\n    \u003cdiv\u003e\n      {error \u0026\u0026 \u003cdiv\u003eError: {error.message}\u003c/div\u003e}\n      {status === \"initializing\" \u0026\u0026 \u003cdiv\u003eInitializing...\u003c/div\u003e}\n      \u003cbutton onClick={fetchData} disabled={!actor}\u003eFetch Data\u003c/button\u003e\n      {isAuthenticated \u0026\u0026 \u003cspan\u003eAuthenticated\u003c/span\u003e}\n    \u003c/div\u003e\n  );\n}\n```\n\nThe hook function also exposes non-React helpers that can be used outside components, for example in route guards:\n\n```ts\n// Wait for the hook to finish initial setup\nawait useBackendActor.ensureInitialized();\n// Inspect helper predicates\nif (useBackendActor.isInitializing()) { /* still initializing */ }\nif (useBackendActor.isSuccess()) { /* initialized successfully */ }\nif (useBackendActor.isError()) { /* initialization failed */ }\n// Check authentication helper\nif (useBackendActor.isAuthenticated()) { /* identity attached */ }\n// Get actor instance (may be undefined if not initialized)\nconst actor = useBackendActor.getActor();\n// Authenticate the hook with an identity\nawait useBackendActor.authenticate(identity);\n```\n\n### Multiple Canisters\n\nCreate a hook for each canister:\n\n```tsx\n// actors.ts\nexport const useBackendOne = createActorHook\u003cBackendOneService\u003e({\n  canisterId: backendOneCanisterId,\n  idlFactory: backendOneIdlFactory,\n});\n\nexport const useBackendTwo = createActorHook\u003cBackendTwoService\u003e({\n  canisterId: backendTwoCanisterId,\n  idlFactory: backendTwoIdlFactory,\n});\n```\n\nAuthenticate each hook when an identity becomes available (in a component):\n\n```tsx\nfunction MultiCanisterComponent() {\n  const { identity } = useInternetIdentity();\n  const backendOne = useBackendOne();\n  const backendTwo = useBackendTwo();\n\n  useEffect(() =\u003e {\n    if (identity) {\n      void backendOne.authenticate(identity);\n      void backendTwo.authenticate(identity);\n    }\n  }, [identity, backendOne, backendTwo]);\n\n  // Use the actors...\n}\n```\n\n### Router integration\n\nWhen using a routing library (e.g. TanStack Router) you can initialize the Internet Identity library and then ensure and authenticate your actor hooks before a route loads.\n\nAvailable functions:\n\n- `ensureAllInitialized(): Promise\u003cvoid\u003e` — wait for all registered actor hooks to finish their initial anonymous setup\n- `authenticateAll(identity: Identity, filterCanisterIds?: string[]): Promise\u003cvoid\u003e` — authenticate all (or a filtered subset) of registered hooks with the provided identity\n- Per-hook helpers attached to the hook function: `useMyActor.ensureInitialized()`, `useMyActor.authenticate(identity)`, `useMyActor.getActor()`, `useMyActor.isAuthenticated()`\n\nBasic example (TanStack Router):\n\n```ts\nimport { createRoute, redirect } from \"@tanstack/react-router\";\nimport { ensureInitialized as ensureIdentityInitialized } from \"ic-use-internet-identity\";\nimport { ensureAllInitialized, authenticateAll } from \"ic-use-actor\";\nimport { useBackendOne, useBackendTwo } from \"./actors\";\n\nconst dashboardRoute = createRoute({\n  getParentRoute: () =\u003e rootRoute,\n  path: \"dashboard\",\n  beforeLoad: async () =\u003e {\n    // 1. Ensure the identity library has finished restoring any cached identity\n    const identity = await ensureIdentityInitialized();\n    if (!identity) {\n      throw redirect({ to: \"/login\" });\n    }\n\n    // 2. Wait for actor hooks to initialize (anonymous agents created)\n    await ensureAllInitialized();\n\n    // 3. Authenticate all registered actor hooks with the restored identity\n    await authenticateAll(identity);\n\n    // Alternatively authenticate specific hooks:\n    // await useBackendOne.ensureInitialized();\n    // await useBackendOne.authenticate(identity);\n    // await useBackendTwo.ensureInitialized();\n    // await useBackendTwo.authenticate(identity);\n  },\n  component: DashboardComponent,\n});\n```\n\nImportant notes:\n\n- Always await the Internet Identity initialization first (`ic-use-internet-identity.ensureInitialized()`).\n- `beforeLoad` runs once during navigation and does not react to later authentication changes — use a reactive component to observe auth changes at runtime.\n\n## Advanced Usage\n\n### Interceptors\n\nAdd request/response interceptors to proxy and process or log interactions with your canister. Interceptors intercept booth outgoing requests and incoming responses as well as errors.\n\n```tsx\nfunction MyComponent() {\n  const { actor, authenticate, setInterceptors } = useBackendActor();\n  const { identity, logout } = useAuthProvider();\n  const navigate = useNavigate();\n\n  // Set up interceptors once - they can access React hooks\n  useEffect(() =\u003e {\n    setInterceptors({\n      // Called before each request\n      onRequest: (data) =\u003e {\n        console.log(`Calling ${data.methodName}`, data.args);\n        // Modify args if needed\n        return data.args;\n      },\n\n      // Called after successful responses\n      onResponse: (data) =\u003e {\n        console.log(`Response from ${data.methodName}`, data.response);\n        // Modify response if needed\n        return data.response;\n      },\n\n      // Called on request errors (e.g., network issues)\n      onRequestError: (data) =\u003e {\n        console.error(`Request error in ${data.methodName}`, data.error);\n        // Transform or handle error\n        return data.error;\n      },\n\n      // Called on response errors - can access React hooks here!\n      onResponseError: (data) =\u003e {\n        console.error(`Response error in ${data.methodName}`, data.error);\n\n        // Check for expired identity and handle it\n        if (data.error.message?.includes(\"delegation expired\")) {\n          logout(); // Call React hook function\n          navigate('/login'); // Use React Router\n        }\n\n        return data.error;\n      },\n    });\n  }, [setInterceptors, logout, navigate]);\n\n  // Authenticate when identity is available\n  useEffect(() =\u003e {\n    if (identity) {\n      authenticate(identity);\n    }\n  }, [identity, authenticate]);\n\n  // ... rest of component\n}\n```\n\n### Error Handling\n\nThe hook provides error state that you can use to handle initialization errors:\n\n```tsx\nfunction MyComponent() {\n  const { actor, error, clearError, authenticate } = useBackendActor();\n  const { identity } = useSiweIdentity();\n\n  if (error) {\n    return (\n      \u003cdiv\u003e\n        \u003cp\u003eError: {error.message}\u003c/p\u003e\n        \u003cbutton onClick={() =\u003e {\n          clearError();\n          if (identity) {\n            authenticate(identity);\n          }\n        }}\u003e\n          Retry\n        \u003c/button\u003e\n      \u003c/div\u003e\n    );\n  }\n\n  // ...\n}\n```\n\n### Custom HTTP Agent Options\n\nConfigure the HTTP agent with custom options:\n\n```tsx\nexport const useBackendActor = createActorHook\u003c_SERVICE\u003e({\n  canisterId,\n  idlFactory,\n  httpAgentOptions: {\n    host: \"https://ic0.app\",\n    credentials: \"include\",\n    headers: {\n      \"X-Custom-Header\": \"value\",\n    },\n  },\n  actorOptions: {\n    callTransform: (methodName, args, callConfig) =\u003e {\n      // Transform calls before sending\n      return [methodName, args, callConfig];\n    },\n    queryTransform: (methodName, args, callConfig) =\u003e {\n      // Transform queries before sending\n      return [methodName, args, callConfig];\n    },\n  },\n});\n```\n\n## API Reference\n\n### createActorHook\n\nCreates a React hook for interacting with an IC canister.\n\n```typescript\nfunction createActorHook\u003cT\u003e(options: CreateActorHookOptions\u003cT\u003e): () =\u003e UseActorReturn\u003cT\u003e\n```\n\n#### Options\n\n| Option | Type | Required | Description |\n|--------|------|----------|-------------|\n| `canisterId` | `string` | Yes | The canister ID |\n| `idlFactory` | `IDL.InterfaceFactory` | Yes | The IDL factory for the canister |\n| `httpAgentOptions` | `HttpAgentOptions` | No | Options for the HTTP agent |\n| `actorOptions` | `ActorConfig` | No | Options for the actor |\n\n\n### Hook Return Value\n\nThe hook returns runtime state and helpers for interacting with the actor:\n\n```typescript\ninterface UseActorReturn\u003cT\u003e {\n  actor: ActorSubclass\u003cT\u003e | undefined;\n\n  // Initialization status: 'initializing' | 'success' | 'error'\n  status: \"initializing\" | \"success\" | \"error\";\n  // Convenience booleans derived from `status`\n  isInitializing: boolean; // status === 'initializing'\n  isSuccess: boolean;      // status === 'success' (actor instance created)\n  isError: boolean;        // status === 'error'\n\n  // Authentication flag (separate from initialization)\n  isAuthenticated: boolean; // whether an identity has been attached\n\n  // Any error that occurred during initialization. Authentication and interceptor errors do not populate this field.\n  error?: Error;\n\n  // Helpers\n  authenticate: (identity: Identity) =\u003e Promise\u003cvoid\u003e;\n  setInterceptors: (interceptors: InterceptorOptions) =\u003e void;\n  reset: () =\u003e void;\n  clearError: () =\u003e void;\n}\n```\n\nNotes:\n- `isSuccess` means the actor instance was successfully created (initialization completed). It does NOT imply the actor has been authenticated — use `isAuthenticated` to check identity attachment.\n- `authenticate(identity)` attaches the identity to the actor's agent (no network calls) and updates `isAuthenticated`.\n\nNon-react helpers (attached to the hook function)\n\nEach hook function also exposes helpers you can call outside React (useful for route guards):\n\n- `ensureInitialized(): Promise\u003cActorSubclass\u003cT\u003e | undefined\u003e` — wait for the hook's initial actor initialization to complete.\n- `authenticate(identity: Identity): Promise\u003cvoid\u003e` — attach an identity to the actor (same as the hook method).\n- `getActor(): ActorSubclass\u003cT\u003e | undefined` — get the current actor instance (may be proxied by interceptors).\n- `isAuthenticated(): boolean` — whether an identity is attached.\n- `isInitializing(): boolean` — predicate for initialization in progress.\n- `isSuccess(): boolean` — predicate for initialization success.\n- `isError(): boolean` — predicate for initialization error.\n\nExample:\n\n```ts\nawait useBackendActor.ensureInitialized();\nif (!useBackendActor.isSuccess()) throw new Error('Actor failed to initialize');\nif (!useBackendActor.isAuthenticated()) await useBackendActor.authenticate(identity);\nconst actor = useBackendActor.getActor();\n```\n\nProperty summary\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `actor` | `ActorSubclass\u003cT\u003e | undefined` | The actor instance (initialized with anonymous agent by default) |\n| `status` | `\"initializing\" | \"success\" | \"error\"` | Initialization status of the actor (only initialization) |\n| `isInitializing` | `boolean` | `status === \"initializing\"` |\n| `isSuccess` | `boolean` | `status === \"success\"` (actor instance created) |\n| `isError` | `boolean` | `status === \"error\"` |\n| `isAuthenticated` | `boolean` | Whether an identity has been attached to the actor |\n| `error` | `Error | undefined` | Any error that occurred during initialization. Authentication and interceptor errors do not populate this field. |\n| `authenticate` | `(identity: Identity) =\u003e Promise\u003cvoid\u003e` | Attach an identity to the actor's agent |\n| `setInterceptors` | `(interceptors: InterceptorOptions) =\u003e void` | Apply request/response interceptors to the actor |\n| `reset` | `() =\u003e void` | Reset the actor state and reinitialize |\n| `clearError` | `() =\u003e void` | Clear stored error state |\n\n### Global Helpers\n\nHelpers that operate across all registered hook instances (useful when your app creates multiple actor hooks):\n\n- `ensureAllInitialized(): Promise\u003cvoid\u003e` — waits for every registered hook to finish its initial anonymous setup. Useful in route guards where you want all actor hooks ready.\n\n- `authenticateAll(identity: Identity, filterCanisterIds?: string[]): Promise\u003cvoid\u003e` — attaches the provided identity to all registered hooks; if `filterCanisterIds` is supplied only hooks whose `canisterId` is included will be authenticated. Throws if any hook's authentication fails.\n\n- `authenticateCanister(identity: Identity, canisterId: string): Promise\u003cvoid\u003e` — convenience wrapper to authenticate hooks for a specific canister id.\n\nExample (router integration):\n\n```ts\nimport { ensureAllInitialized, authenticateAll } from 'ic-use-actor';\nimport { ensureInitialized as ensureIdentityInitialized } from 'ic-use-internet-identity';\n\nconst identity = await ensureIdentityInitialized();\nif (!identity) throw redirect('/login');\nawait ensureAllInitialized();\nawait authenticateAll(identity);\n```\n\n\n### Notes on global helpers\n\n- `ensureAllInitialized` only waits for the initial anonymous `HttpAgent` + actor creation — it does not authenticate hooks.\n- `authenticateAll` will call each hook's `authenticate` helper which updates the per-hook `isAuthenticated` flag.\n\n## Migration from v0.1.x\n\nIf you're upgrading from v0.1.x, check out the [Migration Guide](MIGRATION.md) for detailed instructions on updating your code to use the new API.\n\n## Author\n\n- [kristofer@fmckl.se](mailto:kristofer@fmckl.se)\n- Twitter: [@kristoferlund](https://twitter.com/kristoferlund)\n- Discord: kristoferkristofer\n- Telegram: [@kristoferkristofer](https://t.me/kristoferkristofer)\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nMIT\n\n[version-image]: https://img.shields.io/npm/v/ic-use-actor.svg?style=flat-square\n[dl-image]: https://img.shields.io/npm/dm/ic-use-actor.svg?style=flat-square\n[npm-link]: https://www.npmjs.com/package/ic-use-actor\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkristoferlund%2Fic-use-actor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkristoferlund%2Fic-use-actor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkristoferlund%2Fic-use-actor/lists"}