TechBlog
首页分类标签搜索关于

© 2025 TechBlog. All rights reserved.

react中使用复制的功能

12/22/2025
未分类#前端#React#Javascript

微信小程序星海飞驰

react中使用复制的功能

在react中使用最新的navigator.clipboard.writeText(url)方法来实现复制的功能

navigator.clipboard.writeText() 是现代 Web 开发中用于将文本写入系统剪贴板的标准 API。相比旧的 document.execCommand('copy') 方法,它更安全、更高效,且不依赖 DOM 元素。


import React, { FC, useRef, useMemo } from 'react'
import { Space, Button, Typography, Input, Tooltip, message, Popover } from 'antd'
import type { InputRef } from 'antd'
import { LeftOutlined, CopyOutlined, QrcodeOutlined } from '@ant-design/icons'
import { useNavigate, useParams } from 'react-router-dom'
import QRCode from 'qrcode.react'
import useGetPageInfo from '../../../hooks/useGetPageInfo'
import styles from './StatHeader.module.scss'

const { Title } = Typography

/**
 * 统计页头部组件
 * 负责展示问卷标题、返回按钮、分享链接/二维码以及编辑入口
 */
const StatHeader: FC = () => {
  // 获取路由跳转函数
  const nav = useNavigate()
  // 获取路由参数中的问卷 ID
  const { id } = useParams()

  // 使用自定义 Hook 获取问卷页面信息(标题、是否已发布)
  const { title, isPublished } = useGetPageInfo()

  // 创建一个 ref 来引用链接输入框,用于获取 URL 值(虽然这里直接拼接了,但保留了通过 DOM 获取的逻辑)
  const urlInputRef = useRef<InputRef>(null)

  /**
   * 复制链接到剪贴板的异步函数
   * 使用现代 Clipboard API 实现
   */
  async function copy() {
    const elem = urlInputRef.current
    if (elem == null) return
    
    // 从输入框元素中获取 URL 值
    // 注意:这里使用了可选链操作符,防止 elem.input 为 undefined
    const url = elem.input?.value || ''
    
    try {
      // 调用浏览器原生 API 写入剪贴板
      await navigator.clipboard.writeText(url)
      message.success('拷贝成功')
    } catch (err) {
      // 捕获并处理写入失败的情况(如权限被拒、非安全上下文等)
      message.error('拷贝失败')
      console.error('Failed to copy: ', err)
    }
  }

  // 使用 useMemo 进行性能优化:仅当 id 或 isPublished 变化时重新计算
  // 目的:避免每次渲染都重新创建复杂的 JSX 元素(包含 Input, Button, QRCode),减少重渲染开销
  const LinkAndQRCodeElem = useMemo(() => {
    // 如果问卷未发布,则不显示分享组件
    if (!isPublished) return null

    // 拼接问卷的访问 URL (基于 C 端访问规则)
    // 注意:此处为本地开发环境地址,生产环境需替换为正式域名
    const url = `http://localhost:3000/question/${id}`

    // 定义二维码弹窗的内容组件
    const QRCodeElem = (
      <div style={{ textAlign: 'center' }}>
        {/* 使用 qrcode.react 库生成二维码,指定大小为 150px */}
        <QRCode value={url} size={150} />
      </div>
    )

    // 返回包含链接输入框、复制按钮和二维码按钮的工具栏
    return (
      <Space>
        {/* 只读输入框显示链接 */}
        <Input 
          value={url} 
          style={{ width: '300px' }} 
          ref={urlInputRef} 
        />
        
        {/* 复制链接按钮,带 Tooltip 提示 */}
        <Tooltip title="拷贝链接">
          <Button 
            icon={<CopyOutlined />} 
            onClick={copy} // 点击触发复制逻辑
          />
        </Tooltip>
        
        {/* 二维码弹窗按钮 */}
        <Popover content={QRCodeElem}>
          <Button icon={<QrcodeOutlined />} />
        </Popover>
      </Space>
    )
  }, [id, isPublished]) // 依赖数组:只有当问卷 ID 或发布状态改变时才重新执行

  return (
    <div className={styles['header-wrapper']}>
      <div className={styles.header}>
        {/* 左侧:返回按钮和问卷标题 */}
        <div className={styles.left}>
          <Space>
            <Button 
              type="link" 
              icon={<LeftOutlined />} 
              onClick={() => nav(-1)} // 点击返回上一页
            >
              返回
            </Button>
            <Title level={3} style={{ margin: 0 }}>{title}</Title>
          </Space>
        </div>
        
        {/* 中间:链接与二维码分享区域(仅在发布后显示) */}
        <div className={styles.main}>{LinkAndQRCodeElem}</div>
        
        {/* 右侧:编辑问卷入口 */}
        <div className={styles.right}>
          <Button 
            type="primary" 
            onClick={() => nav(`/question/edit/${id}`)} // 跳转到编辑页面
          >
            编辑问卷
          </Button>
        </div>
      </div>
    </div>
  )
}

export default StatHeader

微信小程序星海飞驰