@solana/react 通过单一 provider 将 Kit 客户端发布到组件树中,各 hook 从中读取 RPC 数据、订阅及钱包状态。浏览器钱包 hook 位于 @solana/kit-plugin-wallet/react,读取同一个 provider —— 无需独立的钱包 provider。
安装
$npm install @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-wallet @solana/react
构建客户端
在模块作用域、React 树之外,仅构建一次客户端。在浏览器中,walletSigner 使已连接的钱包同时成为手续费支付方和身份标识。
import { createClient } from "@solana/kit";import { solanaRpc } from "@solana/kit-plugin-rpc";import { walletSigner } from "@solana/kit-plugin-wallet";export const client = createClient().use(walletSigner({ chain: "solana:devnet" })).use(solanaRpc({ rpcUrl: "https://api.devnet.solana.com" }));export type AppClient = Awaited<typeof client>;
walletSigner 添加 client.wallet(Wallet Standard 发现与连接),solanaRpc 添加 client.rpc 及 client.sendTransaction 辅助方法。将客户端类型包裹在 Awaited<> 中导出,以确保在后续添加异步解析插件时类型仍然正确,并将该 AppClient 传入每个 useClient 调用。
只需包裹一次组件树
ClientProvider 发布客户端。数据 hook(useRequest、useSubscription、useTrackedData)通过 useClient 读取它。下方的钱包 hook 需要将同一客户端作为第一个参数显式传入。
"use client";import { ClientProvider } from "@solana/react";import { client } from "./client";export function Providers({ children }: { children: React.ReactNode }) {return <ClientProvider client={client}>{children}</ClientProvider>;}
钱包 Hook
钱包 hook 需要一个安装了钱包插件(如 walletSigner)的客户端。通过 useClient<AppClient>() 获取该客户端,并将其作为每个 hook 的第一个参数传入 —— 这样可实现端到端的完整类型安全,而无需依赖 context 查找。
useWallets(client)—— 为客户端所在链发现的 Wallet Standard 钱包。useWalletStatus(client)——"pending" | "disconnected" | "connecting" | "connected" | "disconnecting" | "reconnecting"。useConnectedWallet(client)—— 返回{ account, signer, wallet }或null。signer对只读钱包为null。useConnect(client)/useDisconnect(client)—— 返回一个ActionResult,其dispatch(及dispatchAsync)执行操作,isRunning、status、data、error和reset追踪操作结果。
"use client";import {useConnect,useConnectedWallet,useDisconnect,useWallets,useWalletStatus} from "@solana/kit-plugin-wallet/react";import { useClient } from "@solana/react";import type { AppClient } from "./client";function WalletPanel() {const client = useClient<AppClient>();const status = useWalletStatus(client);const wallets = useWallets(client);const connected = useConnectedWallet(client);const connect = useConnect(client);const disconnect = useDisconnect(client);if (status === "pending") return null; // wait out auto-reconnectif (connected) {return (<div><p>{connected.account.address}</p><button onClick={() => disconnect.dispatch()}>Disconnect</button></div>);}return (<div>{wallets.map((wallet) => (<buttonkey={wallet.name}disabled={connect.isRunning}onClick={() => connect.dispatch(wallet)}>Connect {wallet.name}</button>))}</div>);}
发送交易
useClient 返回你发布的同一客户端。使用生成的程序客户端构建指令,并通过 client.sendTransaction 发送。
"use client";import { address, lamports } from "@solana/kit";import { getTransferSolInstruction } from "@solana-program/system";import { useClient } from "@solana/react";import { useConnectedWallet } from "@solana/kit-plugin-wallet/react";import type { AppClient } from "./client";function SendSol({ destination }: { destination: string }) {const client = useClient<AppClient>();const connected = useConnectedWallet(client);async function handleSend() {if (!connected?.signer) return;const transfer = getTransferSolInstruction({source: connected.signer,destination: address(destination),amount: lamports(10_000_000n) // 0.01 SOL});const result = await client.sendTransaction([transfer]);console.log("sent", result.context.signature);}return (<button disabled={!connected?.signer} onClick={handleSend}>Send 0.01 SOL</button>);}
将导出的 AppClient 类型传入 useClient,可使 hook 获得经插件扩展的完整客户端,从而为 client.rpc 和 client.sendTransaction 提供完整的类型支持。
读取数据
@solana/react 提供了通过客户端读取数据的 hook:
useRequest(source)—— 单次 RPC 读取;返回{ data, error, status, refresh }。useSubscription(source)—— 实时订阅;返回{ data, error, reconnect, status }。useTrackedData(spec)—— 以 RPC 为数据源、由订阅保持最新的值。
"use client";import { address } from "@solana/kit";import { useClient, useRequest } from "@solana/react";import { useMemo } from "react";import type { AppClient } from "./client";function Balance({ owner }: { owner: string }) {const client = useClient<AppClient>();const source = useMemo(() => client.rpc.getBalance(address(owner)),[client, owner]);const { data, status, refresh } = useRequest(source);if (status === "fetching") return <p>Loading…</p>;if (status === "error") return <p role="alert">RPC error</p>;return (<div><p>Lamports: {data?.value.toString()}</p><button onClick={() => refresh()}>Refresh</button></div>);}
使用 useMemo(或对函数使用 useCallback)对 source 进行记忆化处理,使 hook 仅在输入变化时重新请求,而非每次渲染都触发。
对于缓存与重新验证,@solana/react/swr 和 @solana/react/query 子路径分别为 SWR 和 TanStack Query 封装了相同的 hook。
Solana 开发者常用模式
- 单一 provider:
ClientProvider是你唯一需要的 provider;通过useClient<AppClient>()获取客户端,并将其传入钱包 hook。 - 服务端组件兼容:仅对调用 hook 的叶子组件标记
"use client";服务端读取可使用不带钱包的普通 Kit RPC 客户端。 - 测试:通过
ClientProvider发布模拟客户端,以模拟钱包和 RPC 响应。
两个 hook 系列,名称相同
useSignIn 和 useSignMessage 同时存在于 @solana/react(接受 UiWalletAccount 参数)和 @solana/kit-plugin-wallet/react(接受 client,返回 ActionResult)中。请使用 @solana/kit-plugin-wallet/react 版本配合上述客户端模式;较旧的 @solana/react 钱包 hook 正逐步被取代。
请将本指南与 Kit 客户端概览 配合阅读,以深入了解每个 hook 所读取的客户端。
Is this page helpful?