AI agents: the machine-readable documentation index for this site is at https://docs.ckbccc.com/llms.txt. Append ".md" to any documentation page URL to fetch its canonical Markdown source, which is preferred over rendered HTML for retrieval, indexing, question answering, and code generation.

Product-specific agent operating guidance (read before generating CKB/CCC code): https://docs.ckbccc.com/skill.md

包

@ckb-ccc/connector-react

面向 React 的连接器,提供 Provider 组件和 Hook,用于 CKB 钱包集成。

Edit on GitHub

@ckb-ccc/connector-react 是 React 和 Next.js 应用的推荐集成方案。它提供一个 Context Provider、一个暴露完整连接器状态的 useCcc Hook,以及一个在钱包已连接时返回当前 signer 的 useSigner Hook。

安装

@ckb-ccc/connector-react npm version@ckb-ccc/connector-react npm downloads per week
npm install @ckb-ccc/connector-react

快速上手

将 Provider 放在应用的根层级,位于所有需要访问钱包的组件之上。

import { ccc } from "@ckb-ccc/connector-react";

export default function App({ children }) {
  return (
    <ccc.Provider>
      {children}
    </ccc.Provider>
  );
}

在任意子组件中调用 useCcc(),即可调用其 open 方法打开钱包选择界面并读取已连接钱包的状态。

import { ccc } from "@ckb-ccc/connector-react";

export function ConnectButton() {
  const { open, wallet, signerInfo } = ccc.useCcc();

  return (
    <div>
      <button onClick={open}>
        {wallet ? `已连接:${wallet.name}` : "连接钱包"}
      </button>
      {signerInfo && <Address />}
    </div>
  );
}

function Address() {
  const { signerInfo } = ccc.useCcc();
  const [address, setAddress] = React.useState("");

  React.useEffect(() => {
    signerInfo?.signer.getRecommendedAddress().then(setAddress);
  }, [signerInfo]);

  return <p>{address}</p>;
}

Provider 属性

function CccProvider({ children }: { children: React.ReactNode }) {
  const [clientOptions, setClientOptions] =
    useState<{ name: string; client: ccc.Client }[]>();

  useEffect(() => {
    const owner = ccc.OwnerAggregated.from([
      ccc.ClientPublicTestnet.open(),
      ccc.ClientPublicMainnet.open(),
    ] as const);
    const [testnet, mainnet] = owner.value;
    // Client 必须在 commit 后打开,避免 aborted render 泄漏资源。
    // eslint-disable-next-line react-hooks/set-state-in-effect
    setClientOptions([
      { name: "Testnet", client: testnet },
      { name: "Mainnet", client: mainnet },
    ]);
    return () => void owner.dispose().catch(() => {});
  }, []);

  if (!clientOptions) return null;

  return (
    <ccc.Provider
      hideMark={false}
      name="My App"
      icon="https://example.com/icon.png"
      khieRelayAddress="/dns4/relay.example/tcp/443/wss"
      signerFilter={async (signerInfo, wallet) => true}
      clientOptions={clientOptions}
    >
      {children}
    </ccc.Provider>
  );
}
属性名类型说明
childrenReactNode应用组件树
connectorPropsHTMLAttributes<{}>?传递给 connector 元素的额外 Props
hideMarkboolean?隐藏连接器界面中的"Powered by CCC"标识
hideKhieboolean?隐藏 Khie 钱包连接入口
namestring?显示在钱包选择界面中的应用名称
iconstring?显示在钱包选择界面中的应用图标 URL
khieRelayAddressstring?Khie 使用的默认 Relay multiaddr
signerFilter(signerInfo, wallet) => Promise<boolean>筛选在界面中显示的钱包与 signer 组合
signersControllerccc.SignersController?自定义 Signers Controller(高级用法)
defaultClientccc.Client?借用的初始客户端;优先于 clientOptions[0]
clientOptions{ icon?, client, name }[]?借用的网络选项;未传 defaultClient 时首项为默认值

调整 Connector 样式

通过 connectorProps.style 传入 CSS 自定义属性。Provider 已提供一套完整的 浅色默认主题,因此只需要覆盖当前主题会改变的值。

<ccc.Provider
  connectorProps={{
    style: {
      color: "#e6eef2",
      "--background": "#11181c",
      "--btn-primary": "#171d21",
      "--btn-primary-hover": "#5bcefa",
      "--btn-color": "#e6eef2",
      "--btn-color-hover": "#070a0c",
      "--tip-color": "#76858d",
      "--tip-color-hover": "#31515f",
    } as React.CSSProperties,
  }}
>
  {children}
</ccc.Provider>

完整变量清单、默认值及回退行为请参阅 @ckb-ccc/connector 样式参考。

useCcc() Hook

const {
  isOpen,      // boolean——钱包选择弹窗是否处于打开状态
  open,        // () => void——打开钱包选择界面
  close,       // () => void——关闭钱包选择界面
  disconnect,  // () => void——断开当前钱包连接
  setClient,   // (owner: ccc.Owner<ccc.Client>) => void——转移所有权并切换客户端
  client,      // ccc.Client——当前网络客户端
  wallet,      // ccc.Wallet | undefined——已连接的钱包
  signerInfo,  // ccc.SignerInfo | undefined——已连接的 signer
} = ccc.useCcc();

在 <ccc.Provider> 树之外调用此 Hook 会抛出错误。

通过 defaultClient 或 clientOptions 传入的客户端仍由调用方持有并负责清理。 调用 setClient(owner) 会将所有权转移给 Provider;该 owner 会在下次调用 setClient 或 Provider 卸载时清理。

useBorrowedOrOwned() Hook

当资源可能从上层借用、而上层未提供时又需要创建内部 fallback,可以使用此 Hook:

function openTestnetClient() {
  return ccc.ClientPublicTestnet.open();
}

function ClientConsumer({ client: borrowed }: { client?: ccc.Client }) {
  const client = ccc.useBorrowedOrOwned(borrowed, openTestnetClient);
  if (!client) return null; // fallback 会在组件 commit 后创建。

  return <App client={client} />;
}

Hook 不会释放借用值。fallback Owner 与借用值相互独立,可以持续复用;只有 open 函数变化时才会重新创建,并在被替换或组件卸载时释放。应保持 open 的引用稳定。

完整示例

"use client"; // Next.js App Router 必须添加

import { ccc } from "@ckb-ccc/connector-react";
import { useState, useEffect } from "react";

function Layout({ children }: { children: React.ReactNode }) {
  return (
    <ccc.Provider name="My CKB App">
      <Header />
      {children}
    </ccc.Provider>
  );
}

function Header() {
  const { open, disconnect, wallet, signerInfo, client } = ccc.useCcc();
  const [address, setAddress] = useState("");

  useEffect(() => {
    if (!signerInfo) {
      setAddress("");
      return;
    }
    signerInfo.signer.getRecommendedAddress().then(setAddress);
  }, [signerInfo]);

  return (
    <header>
      {wallet ? (
        <>
          <span>{address}</span>
          <button onClick={disconnect}>断开连接</button>
        </>
      ) : (
        <button onClick={open}>连接钱包</button>
      )}
    </header>
  );
}

筛选钱包

通过 signerFilter 只显示特定类型的钱包:

import { ccc } from "@ckb-ccc/connector-react";

// 仅显示 CKB 原生钱包
<ccc.Provider
  signerFilter={async (signerInfo, wallet) => {
    return signerInfo.signer.type === ccc.SignerType.CKB;
  }}
>
  {children}
</ccc.Provider>

Next.js(App Router)

CCC 的连接器依赖 React Context 和浏览器 API,只能在客户端运行。所有导入 @ckb-ccc/connector-react 或渲染 <ccc.Provider> 的文件,均须在文件顶部添加 "use client"。

"use client";

import { ccc } from "@ckb-ccc/connector-react";

在 Next.js App Router 中忘记添加 "use client" 会导致运行时报错: TypeError: (0, react....createContext) is not a function。

Last updated on

On this page