You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中使用usePopper在表格映射中仅渲染单个Popper的方法

解决Reactstrap表格映射中Popper批量触发的问题

核心问题原因

你目前用单个isOpen布尔值控制所有行的Popper显示,点击任何一行都会触发全局状态变更,导致所有Popper同时弹出。解决的核心思路是用行的唯一标识精准控制单个Popper的显示状态,而非依赖全局布尔值。

具体实现步骤

1. 修改状态管理

把原来的单个isOpen状态替换为activeRowId,用来记录当前需要显示Popper的行ID(初始值设为null):

const [activeRowId, setActiveRowId] = useState(null);

2. 编写状态切换逻辑

写一个切换函数,接收当前行的ID作为参数,判断是否要打开/关闭对应行的Popper:

const togglePopper = (rowId) => {
  // 如果当前行已激活则关闭,否则激活当前行
  setActiveRowId(prev => prev === rowId ? null : rowId);
};

3. 封装行组件(推荐)

把每行的内容和Popper逻辑封装成独立子组件,避免映射中重复代码,同时让每个行拥有独立的Popper实例(解决react-popper的引用冲突):

子组件TableRowWithPopper.jsx:

import { useRef, useState } from 'react';
import { usePopper } from 'react-popper';

export default function TableRowWithPopper({ item, isOpen, onToggle }) {
  // 每个行独立的引用
  const referenceRef = useRef(null);
  const [popperElement, setPopperElement] = useState(null);

  // 初始化当前行的Popper实例
  const { styles, attributes } = usePopper(
    referenceRef.current,
    popperElement,
    {
      placement: 'top', // 自定义Popper位置
      modifiers: [{ name: 'offset', options: { offset: [0, 8] } }]
    }
  );

  return (
    <tr>
      {/* 触发Popper的单元格 */}
      <td ref={referenceRef} onClick={onToggle} style={{ cursor: 'pointer' }}>
        {item.name}
        {/* 仅当当前行激活时渲染Popper */}
        {isOpen && (
          <div
            ref={setPopperElement}
            style={{ ...styles.popper, backgroundColor: '#fff', border: '1px solid #ddd', padding: '8px', borderRadius: '4px' }}
            {...attributes.popper}
          >
            {item.detail}
          </div>
        )}
      </td>
      <td>{item.value}</td>
    </tr>
  );
}

4. 在表格中映射组件

在父组件的表格中,遍历数据并传递对应的状态和事件:

import { Table } from 'reactstrap';
import TableRowWithPopper from './TableRowWithPopper';

export default function YourTable({ dataList }) {
  const [activeRowId, setActiveRowId] = useState(null);

  const togglePopper = (rowId) => {
    setActiveRowId(prev => prev === rowId ? null : rowId);
  };

  return (
    <Table striped bordered hover>
      <thead>
        <tr>
          <th>名称</th>
          <th>数值</th>
        </tr>
      </thead>
      <tbody>
        {dataList.map(item => (
          <TableRowWithPopper
            key={item.id}
            item={item}
            isOpen={activeRowId === item.id}
            onToggle={() => togglePopper(item.id)}
          />
        ))}
      </tbody>
    </Table>
  );
}

额外优化(可选)

如果需要点击页面其他区域自动关闭Popper,可以在父组件添加全局点击监听:

import { useEffect } from 'react';

// 父组件内添加
useEffect(() => {
  const handleClickOutside = (e) => {
    // 判断点击目标不在任何触发元素或Popper内时,关闭所有Popper
    const triggerElements = document.querySelectorAll('[data-popper-trigger]');
    const popperElements = document.querySelectorAll('[data-popper-content]');
    const isClickInside = [...triggerElements, ...popperElements].some(el => el.contains(e.target));
    if (!isClickInside) setActiveRowId(null);
  };

  document.addEventListener('click', handleClickOutside);
  return () => document.removeEventListener('click', handleClickOutside);
}, []);

记得给触发元素和Popper元素加上对应的data-popper-trigger、data-popper-content属性。


内容的提问来源于stack exchange,提问作者giosan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 04:37:45