在 React 中复制到剪贴板的正确方式 (2024)

发布: (2025年12月19日 GMT+8 11:46)
5 min read
原文: Dev.to

Source: Dev.to

Cover image for “The Right Way to Copy to Clipboard in React (2024)”

Samitha Widanage

复制到剪贴板看起来很简单,对吧?点击一个按钮,文本就会进入剪贴板。完成。

但是如果你曾经尝试在 React 中实现它,你会知道并不是那么直截了当。浏览器 API 已经演变,需要旧版回退,而且正确处理状态也需要思考。

我构建了 @samithahansaka/clipboard 来一次性解决这个问题。以下是原因以及你如何使用它。

现有解决方案的问题

我评估了流行的选项:

Library / ApproachWeekly DownloadsMain Drawbacks
react-copy-to-clipboard500K+• 没有 hooks API(需要类组件包装器)
• 不支持富(HTML)内容
• 最近一次有意义的更新是多年以前
手动实现(copy‑paste from Stack Overflow)• 未正确处理回退方案
• 没有 “已复制!” 的状态管理
• 在 Next.js、Remix 等框架下存在 SSR 问题
use-clipboard-copy• 不支持 HTML 内容
• 配置选项有限

结论: 我需要一些 现代轻量级完整 的方案。

介绍 @samithahansaka/clipboard

npm install @samithahansaka/clipboard

有何不同

特性此库其他
Hooks API有时
Component API有时
HTML/Rich content
TypeScript✅ 一等部分
打包体积~1 KB2‑5 KB
依赖0各不相同
SSR 安全经常出错

基本用法

import { useCopyToClipboard } from '@samithahansaka/clipboard';

function CopyButton() {
  const { copy, copied } = useCopyToClipboard();

  return (
    <button onClick={() => copy('Hello, World!')}>
      {copied ? '✓ Copied!' : 'Copy'}
    </button>
  );
}

就是这样。copied 状态会在 2 秒后自动重置。

实际示例:代码块组件

import { useCopyToClipboard } from '@samithahansaka/clipboard';

function CodeBlock({ code, language }: { code: string; language: string }) {
  const { copy, copied, error } = useCopyToClipboard({
    successDuration: 2000,
  });

  return (
    <div className="code-block">
      <div className="header">
        <span>{language}</span>
        <button
          onClick={() => copy(code)}
          className={copied ? 'success' : ''}
        >
          {copied ? '✓ Copied!' : 'Copy'}
        </button>
      </div>
      <pre>{code}</pre>
      {error && <div className="error">Copy failed</div>}
    </div>
  );
}

/* Additional snippet example */
{/* {error ? 'Failed!' : copied ? 'Copied!' : 'Copy'} */}

该组件展示了一个代码片段,并配有复制按钮,点击后会在 2 秒 内显示成功状态,同时能够优雅地处理复制错误。

复制富 HTML 内容

这是大多数库的短板所在。需要复制格式化文本,使其在 Google 文档或电子邮件中能够正确粘贴吗?

const { copy } = useCopyToClipboard();

// 同时复制纯文本 **AND** HTML
copy({
  text: 'Hello, World!',          // 纯文本应用的回退方案
  html: '**Hello, World!**'       // 兼容应用的富内容
});

粘贴到:

  • 记事本 / 终端: Hello, World!
  • Google 文档 / 邮件: Hello, World!(加粗)

与 Toast 库集成

无缝兼容任何通知库:

import { useCopyToClipboard } from '@samithahansaka/clipboard';
import { toast } from 'sonner'; // or react-hot-toast, etc.

function ShareButton({ url }: { url: string }) {
  const { copy } = useCopyToClipboard({
    onSuccess: () => toast.success('Link copied!'),
    onError: () => toast.error('Failed to copy'),
  });

  return (
    <button onClick={() => copy(url)}>
      Share
    </button>
  );
}

简单情况的组件 API

更喜欢渲染属性吗?还有组件 API:

import { CopyButton } from '@samithahansaka/clipboard';

function App() {
  return (
    <CopyButton>
      {/* children can be a custom button or any element */}
      Copy
    </CopyButton>
  );
}
<CopyButton>
  {({ copy, copied }) => (
    <button onClick={copy}>
      {copied ? 'Done!' : 'Copy'}
    </button>
  )}
</CopyButton>

工作原理概述

  1. 现代浏览器 (Chrome 76+, Firefox 63+, Safari 13.1+):
    使用 navigator.clipboard.write() 实现完整的 HTML 支持。
  2. 较旧的现代浏览器:
    回退到 navigator.clipboard.writeText() 进行纯文本复制。
  3. 传统浏览器 (IE 11, 旧版 Safari):
    回退到使用隐藏的 <textarea> 并调用 document.execCommand('copy')

您无需考虑这些细节,它会自动工作。

TypeScript 支持

完整的类型定义已包含:

import {
  useCopyToClipboard,
  CopyButton,
  type CopyContent,
  type ClipboardItem,
} from '@samithahansaka/clipboard';

// TypeScript knows exactly what `copy()` accepts
const { copy } = useCopyToClipboard();

copy('string');                         // ✅
copy({ text: 'hi' });                  // ✅
copy({ text: 'hi', html: '**hi**' }); // ✅
copy(123);                             // ❌ Type error

SSR‑安全

开箱即用,兼容 Next.jsRemixGatsby 或任何 SSR 框架:

  • 没有 “window is not defined” 错误。
  • 不需要动态导入。
  • 直接正常使用即可。
import { useCopyToClipboard } from '@samithahansaka/clipboard';

包大小

整个库的压缩后大小约为 ~1 KB。作比较:

大小(gzip后)
@samithahansaka/clipboard~1 KB
react-copy-to-clipboard~2 KB
use-clipboard-copy~3 KB
Back to Blog

相关文章

阅读更多 »

终极 React Hooks 指南

!TenE 组织 个人资料图片 https://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads....