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

React中如何正确切换类名?按钮选中态类名被移除问题排查

React中Popover按钮高亮类被移除的解决方案

问题根源

React的虚拟DOM渲染机制会在组件重渲染时,覆盖所有手动操作DOM添加的类名——你用原生s-popover控制器直接给按钮加is-selected,但React只认它自己通过props/state管理的DOM属性,所以重渲染后手动加的类就没了。

解决办法(推荐React数据流方式)

1. 用React状态管理按钮选中状态

在组件里添加状态变量跟踪popover的开闭状态,把这个状态绑定到按钮的active属性(或者直接绑定类名):

import { useState, useEffect } from 'react';

function YourComponent({ id, iconName, children, nCols }) {
  const buttonId = `popover-button-${id}`;
  const popoverId = `popover-content-${id}`;
  // 用state跟踪popover是否打开
  const [isPopoverOpen, setIsPopoverOpen] = useState(false);

  return (
    <div
      data-controller="s-popover"
      data-s-popover-placement="bottom"
      data-s-popover-reference-selector={`#${buttonId}`}
      // 监听popover的toggle事件同步状态
      onSPopoverToggle={(e) => setIsPopoverOpen(e.detail.isOpen)}
    >
      <MenuButton
        id={buttonId}
        data-key={id}
        iconName={iconName}
        command={() => true}
        ariaControls={popoverId}
        dataAction="s-popover#toggle"
        data-controller="s-tooltip"
        // 用React状态控制active属性,对应is-selected类
        active={isPopoverOpen}
      />
      <MenuPopover id={popoverId} children={children} nCols={nCols} />
    </div>
  );
}

2. 同步原生控制器与React状态

如果s-popover控制器会触发自定义事件(比如s-popover:toggle),通过useEffect监听事件更新React状态:

useEffect(() => {
  const container = document.querySelector(`[data-controller="s-popover"][data-s-popover-reference-selector="#${buttonId}"]`);
  const handleToggle = (e) => {
    setIsPopoverOpen(e.detail.isOpen);
  };
  container.addEventListener('s-popover:toggle', handleToggle);
  // 组件卸载时移除监听
  return () => container.removeEventListener('s-popover:toggle', handleToggle);
}, [buttonId]);

3. 进阶:让React主动控制popover(可选)

如果s-popover支持通过data-s-popover-open属性控制开闭,直接把状态绑定到这个属性,完全由React主导状态:

<div
  data-controller="s-popover"
  data-s-popover-placement="bottom"
  data-s-popover-reference-selector={`#${buttonId}`}
  // 用React状态控制popover的开闭
  data-s-popover-open={isPopoverOpen}
>
  <MenuButton
    // ...其他属性
    active={isPopoverOpen}
    // 自定义toggle函数直接更新state
    command={() => setIsPopoverOpen(prev => !prev)}
  />
  {/* ... */}
</div>

应急方案(不推荐长期使用)

如果暂时没法改数据流,可用useRef直接操作DOM,但这种方法绕开React虚拟DOM,容易引发不一致问题:

const buttonRef = useRef(null);

useEffect(() => {
  const container = document.querySelector(`[data-controller="s-popover"][data-s-popover-reference-selector="#${buttonId}"]`);
  const handleToggle = (e) => {
    if (e.detail.isOpen) {
      buttonRef.current.classList.add('is-selected');
    } else {
      buttonRef.current.classList.remove('is-selected');
    }
  };
  container.addEventListener('s-popover:toggle', handleToggle);
  return () => container.removeEventListener('s-popover:toggle', handleToggle);
}, [buttonId]);

// 在MenuButton上绑定ref
<MenuButton ref={buttonRef} {/* ...其他属性 */} />

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 12:55:02