Dear React Developers,
I’m thrilled to introduce @ic-reactor/react
, a dedicated React library designed to significantly enhance your development experience on the Internet Computer (IC) blockchain. This library is tailored to harness the full potential of React, providing you with a suite of hooks and utilities for seamless state management, authentication, and type-safe interactions with IC actors.
Key Features:
- React Hooks Integration: Seamlessly manage blockchain actor states and authentication within your React apps.
- Type-Safe Actor Interactions: Ensure type safety with your IC actors, thanks to the integration with actor declaration files.
- Candid Adapter: Fetch Candid interface definitions directly from the network(IC or Local), enabling you to adapt to canisters with changing interfaces or to avoid managing Candid files locally.
- Auto-Refresh & Query Capabilities: Keep your data fresh with auto-refreshing capabilities and straightforward querying of IC actors.
Getting Started:
To integrate @ic-reactor/react
into your project, installation is just a command away:
npm install @ic-reactor/react
# OR
yarn add @ic-reactor/react
Once installed, you have many ways to leverage the power of @ic-reactor/react
in your React applications. Let’s explore some of the key features and usage examples to help you get started.
Usage Example:
Dive into @ic-reactor/react
with this simple example, showcasing how to create an actor and use the useQueryCall
hook for fetching data:
// actor.ts
import { createReactor } from "@ic-reactor/react"
import { backend, canisterId, idlFactory } from "./declarations/backend"
export type Backend = typeof backend
export const { useActorStore, useAuth, useQueryCall } = createReactor<Backend>({
canisterId,
idlFactory,
host: "https://localhost:4943",
})
// Balance.tsx
import { useQueryCall } from "./actor"
const Balance = () => {
const principal = useUserPrincipal()
const { call, data, loading, error } = useQueryCall({
functionName: "get_balance",
args: [principal],
})
return (
<div>
<button onClick={call}>{loading ? "Loading..." : "Refresh"}</button>
{data && <p>Balance: {data}</p>}
{error && <p>Error: {error.toString()}</p>}
</div>
)
}
Authentication Example:
Manage user authentication effortlessly with @ic-reactor/react
:
// Login.tsx
import { useAuth } from "./actor"
const Login = () => {
const { login, logout, identity, authenticated } = useAuth()
return (
<div>
{authenticated ? (
<>
<div>Logged in as: {identity.getPrincipal().toText()}</div>
<button onClick={logout}>Logout</button>
</>
) : (
<button onClick={login}>Login</button>
)}
</div>
)
}
Provider-Based Architecture
@ic-reactor/react
includes an AgentProvider
that establishes a shared IC agent for all your components, ensuring efficient and centralized management of your IC connections. Within this, you can use ActorProvider
to specify canisters and manage their state and interactions in a structured manner. This approach is ideal for applications interacting with multiple canisters, as it keeps your code organized and maintainable.
<AgentProvider>
<Login />
<ActorProvider
canisterId="ryjl3-tyaaa-aaaaa-aaaba-cai"
loadingComponent={<div>Loading Icp Ledger...</div>}
>
<ICPBalance />
<ICPTransfer />
</ActorProvider>
<ActorProvider
canisterId="YOUR_CANISTER_ID"
loadingComponent={<div>Loading Note Actor...</div>}
>
<Notes />
<AddNote />
</ActorProvider>
</AgentProvider>
Note: A noteworthy feature of @ic-reactor/react
that deserves special mention is its capability to streamline the management of IDL files. Traditionally, interacting with canisters on the Internet Computer requires you to manually manage IDL files (*.did
files), which define the interface of your canisters. This can be a cumbersome process, especially in complex projects with multiple canisters.
Then you can use the useAuth
, useQueryCall
or useUpdateCall
and other hooks to interact with the canister.
import { useQueryCall, useUserPrincipal } from "@ic-reactor/react"
export const ICPBalance = () => {
const principal = useUserPrincipal()
const { call, data, loading, error } = useQueryCall({
functionName: "icrc1_balance_of",
args: [{ owner: principal, subaccount: [] }],
})
return (
<div>
<h2>ICP Balance:</h2>
<div>
Loading: {loading.toString()}
<br />
Error: {error?.toString()}
<br />
balance:{" "}
{data !== undefined
? JSON.stringify(data, (_, v) =>
typeof v === "bigint" ? v.toString() : v
)
: null}
</div>
<button onClick={call}>Get Balance</button>
</div>
)
}
Creating Your Own Custom Provider
For developers seeking even more control and customization, @ic-reactor/react
enables the creation of your own context providers. This method is type-safe and offers the flexibility to tailor the provider according to your project’s specific needs, allowing for a more customized integration with the IC.
import { createActorContext } from "@ic-reactor/react"
import { backend, canisterId, idlFactory } from "./declarations/backend"
export type Backend = typeof backend
export const {
ActorProvider: NoteActorProvider,
useQueryCall: useNoteQueryCall,
useUpdateCall: useNoteUpdateCall,
} = createActorContext<Backend>({
canisterId,
idlFactory,
})
Documentation and Examples:
For a deep dive into all the features and to get your hands on more examples, check out documentation and examples.
Contributing:
We welcome your feedback and contributions to the @ic-reactor
project. If you have any questions, suggestions, or issues, feel free to reach out to us on GitHub.
For those who don’t use React, you can still check out this thread @ic-reactor/core on the DFINITY forum. It’s designed to streamline development on the Internet Computer, and may still provide useful tools and insights even if you’re not using React.