React JS中窗口大小改变时增减类名的实现及classList报错解决
报错原因
- 监听函数执行时目标DOM元素还未渲染完成/已经被卸载,
document.getElementById返回null,调用classList方法自然抛出空指针错误 - 全局绑定的resize事件没有在组件卸载时清除,会造成内存泄漏,也会在组件销毁后触发时访问不存在的DOM
- React中直接操作原生DOM不符合声明式编程的思路,容易出现状态和视图不同步的问题
正确实现方案
采用状态驱动视图的思路,完全避免直接操作DOM,从根源解决报错,同时满足业务需求:
- 用
useState维护三个状态:是否为移动端宽度、详情面板显隐状态、会话面板显隐状态 - 用
useEffect绑定resize监听,实时更新移动端状态,组件卸载时清除监听 - 在JSX中直接通过状态动态拼接类名,不需要手动操作DOM的classList
完整代码示例
import { useState, useEffect } from 'react'; function YourComponent() { // 定义状态:是否为移动端宽度、详情面板显隐、会话面板显隐 const [isMobile, setIsMobile] = useState(window.innerWidth <= 768); const [showDetails, setShowDetails] = useState(false); const [showConvo, setShowConvo] = useState(false); useEffect(() => { // 防抖函数,减少resize触发频率,优化性能 const debounce = (func, delay = 200) => { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => func.apply(this, args), delay); }; }; // 窗口大小变化处理逻辑 const handleResize = debounce(() => { const currentIsMobile = window.innerWidth <= 768; setIsMobile(currentIsMobile); // 切回大屏时自动重置面板显隐状态,保证大屏下所有面板默认展示 if (!currentIsMobile) { setShowDetails(false); setShowConvo(false); } }); // 绑定resize监听 window.addEventListener('resize', handleResize); // 组件卸载时清除监听,避免内存泄漏 return () => window.removeEventListener('resize', handleResize); }, []); return ( <div className="container"> {/* 永久展示的聊天列表面板 */} <div className={`chats ${isMobile && showConvo ? 'toggle-chats' : ''}`} > 聊天列表内容 </div> {/* 会话面板:大屏默认展示,移动端点击按钮才展示 */} <div className={`convo ${isMobile && !showConvo ? 'toggle-convo' : ''}`} > 会话内容 {isMobile && <button onClick={() => setShowConvo(false)}>返回列表</button>} </div> {/* 详情面板:大屏默认展示,移动端点击按钮才展示 */} <div className={`details ${isMobile && !showDetails ? 'toggle-details' : ''}`} > 详情内容 </div> {/* 移动端控制按钮组 */} {isMobile && ( <div className="btn-group"> <button onClick={() => setShowConvo(!showConvo)}>切换会话</button> <button onClick={() => setShowDetails(!showDetails)}>切换详情</button> </div> )} </div> ); } export default YourComponent;
对应CSS样式示例
/* 大屏默认三个面板全部展示 */ .chats, .convo, .details { display: block; } /* 移动端隐藏类 */ .toggle-convo, .toggle-details, .toggle-chats { display: none; }
内容的提问来源于stack exchange,提问作者testing_dev
相关产品推荐
相关产品推荐

