如何在useEffect中动态更新state?类名变化无法触发状态更新如何解决
最优方案:状态提升(符合React声明式编程规范,无额外性能开销)
你当前实现的核心问题是绕开React状态管理直接操作/监听DOM,且useEffect依赖为空数组仅会在组件挂载时执行一次,自然无法同步后续DOM变化。最优解是把控制类名的状态从FoodList组件提升到当前父组件统一管理:
- 修正当前组件的状态声明(注意原有代码里状态名存在拼写错误
filterExpaned,少了字母d),并把状态和修改方法传递给FoodList组件
const [filterExpanded, setFilterExpanded] = useState<boolean>(false); // 原有监听DOM的useEffect可以完全删除 return ( <> <FoodList filterExpanded={filterExpanded} onToggleFilter={setFilterExpanded} /> </> );
- 修改FoodList组件内部逻辑:
- 把id为
list-panel-toggle的元素的类名和传入的filterExpanded状态绑定 - 内部触发类名变更的按钮点击事件,直接调用父组件传入的
onToggleFilter方法更新状态
// FoodList 内部示例代码 const FoodList = ({ filterExpanded, onToggleFilter }) => { return ( <div> <button id="list-panel-toggle" className={`list-panel-toggle ${filterExpanded ? 'list-panel-toggle--collapsed' : ''}`} onClick={() => onToggleFilter(!filterExpanded)} > 切换面板 </button> </div> ) }
这种方案下状态完全由React管理,两边自动同步,不需要任何DOM操作,性能和可维护性都是最好的。
备选方案:MutationObserver监听DOM变化(仅适用于无法修改FoodList源码的场景)
如果FoodList是第三方组件、你没有权限修改其内部代码,可以用MutationObserver监听目标元素的类名变化,动态同步状态:
const [filterExpanded, setFilterExpanded] = useState<boolean>(false); useEffect(() => { const listPanelToggle = document.getElementById('list-panel-toggle'); if (!listPanelToggle) return; // 初始化状态 setFilterExpanded(listPanelToggle.classList.contains('list-panel-toggle--collapsed')); // 创建监听器 const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.attributeName === 'class') { const hasClass = (mutation.target as Element).classList.contains('list-panel-toggle--collapsed'); setFilterExpanded(hasClass); } }); }); // 监听类名变化 observer.observe(listPanelToggle, { attributes: true, attributeFilter: ['class'] }); // 组件卸载时清理监听器,防止内存泄漏 return () => observer.disconnect(); }, []); return ( <> <FoodList /> </> );
内容的提问来源于stack exchange,提问作者user14649590
相关产品推荐
相关产品推荐

