React

@solana/react 通过单一 provider 将 Kit 客户端发布到组件树中,各 hook 从中读取 RPC 数据、订阅及钱包状态。浏览器钱包 hook 位于 @solana/kit-plugin-wallet/react,读取同一个 provider —— 无需独立的钱包 provider。

安装

Terminal
$
npm install @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-wallet @solana/react

构建客户端

在模块作用域、React 树之外,仅构建一次客户端。在浏览器中,walletSigner 使已连接的钱包同时成为手续费支付方和身份标识。

client.ts
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.rpcclient.sendTransaction 辅助方法。将客户端类型包裹在 Awaited<> 中导出,以确保在后续添加异步解析插件时类型仍然正确,并将该 AppClient 传入每个 useClient 调用。

只需包裹一次组件树

ClientProvider 发布客户端。数据 hook(useRequestuseSubscriptionuseTrackedData)通过 useClient 读取它。下方的钱包 hook 需要将同一客户端作为第一个参数显式传入。

providers.tsx
"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 }nullsigner 对只读钱包为 null
  • useConnect(client) / useDisconnect(client) —— 返回一个 ActionResult,其 dispatch(及 dispatchAsync)执行操作,isRunningstatusdataerrorreset 追踪操作结果。
"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-reconnect
if (connected) {
return (
<div>
<p>{connected.account.address}</p>
<button onClick={() => disconnect.dispatch()}>Disconnect</button>
</div>
);
}
return (
<div>
{wallets.map((wallet) => (
<button
key={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.rpcclient.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 子路径分别为 SWRTanStack Query 封装了相同的 hook。

Solana 开发者常用模式

  • 单一 providerClientProvider 是你唯一需要的 provider;通过 useClient<AppClient>() 获取客户端,并将其传入钱包 hook。
  • 服务端组件兼容:仅对调用 hook 的叶子组件标记 "use client";服务端读取可使用不带钱包的普通 Kit RPC 客户端。
  • 测试:通过 ClientProvider 发布模拟客户端,以模拟钱包和 RPC 响应。

两个 hook 系列,名称相同

useSignInuseSignMessage 同时存在于 @solana/react(接受 UiWalletAccount 参数)和 @solana/kit-plugin-wallet/react(接受 client,返回 ActionResult)中。请使用 @solana/kit-plugin-wallet/react 版本配合上述客户端模式;较旧的 @solana/react 钱包 hook 正逐步被取代。

请将本指南与 Kit 客户端概览 配合阅读,以深入了解每个 hook 所读取的客户端。

Is this page helpful?

Table of Contents

Edit Page
©️ 2026 Solana 基金会版权所有