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
相关产品推荐
相关产品推荐

