React引入原生JS函数时而失效的问题求助
问题描述
开发React网站时引入了外部原生JavaScript文件,点击past、current、future按钮切换对应内容显示/隐藏的功能时灵时不灵,频繁抛出错误:Uncaught TypeError: Cannot read properties of null (reading 'style')。刚接触React,怀疑是文件引入方式有问题,寻求解决办法。
相关代码
exhibitions.js(React组件)
import React from 'react' import './exhibitions.css' import { setPastOn, setCurrentOn, setFutureOn } from './exhibitions-nav.js' export default function Exhibitions() { return ( <main className="main-exhibitions"> <div className="title"> <div className="title-line-left"></div> <h2>exhibitions</h2> <div className="title-line-right"></div> </div> <div className="exhibitions-navbar"> <span className="nav-past" onClick={setPastOn}>past</span> <span className="nav-current" onClick={setCurrentOn}>current</span> <span className="nav-future" onClick={setFutureOn}>future</span> </div> <div className="ex-past"> <h4>past</h4> </div> <div className="ex-current"> <h4>current</h4> </div> <div className="ex-future"> <span className="future-span">No future exhibitions planned at the moment. Keep an eye on this page for updates!</span> </div> </main> ) }
exhibitions-nav.js(引入的原生JS文件)
const pastIndex = document.querySelector('.ex-past'); const currentIndex = document.querySelector('.ex-current'); const futureIndex = document.querySelector('.ex-future'); export function setPastOn(){ pastIndex.style.display = 'block'; currentIndex.style.display = 'none'; futureIndex.style.display = 'none'; } export function setCurrentOn(){ pastIndex.style.display = 'none'; currentIndex.style.display = 'block'; futureIndex.style.display = 'none'; } export function setFutureOn(){ pastIndex.style.display = 'none'; currentIndex.style.display = 'none'; futureIndex.style.display = 'block'; }
相关CSS片段
.ex-past{ display: none; } .ex-current{ display: block; } .ex-future{ display: none; }
问题原因
核心问题是原生JS模块加载时就执行了document.querySelector,这时候React组件还没完成渲染,DOM里根本没有.ex-past、.ex-current、.ex-future这些元素,所以获取到的是null,调用null.style自然会抛出错误。偶尔能工作是因为组件渲染完成后,模块里的变量恰好又能获取到DOM元素,但这种情况完全不可控。
另外,用原生DOM操作修改React渲染的元素,违背了React的设计理念——React是通过状态来管理UI变化的,直接操作DOM会导致React虚拟DOM和真实DOM不一致,引发更多不可预测的问题。
解决方案
改用React的状态管理实现切换逻辑,完全抛弃原生DOM操作的方式:
修改后的exhibitions.js:
import React, { useState } from 'react' import './exhibitions.css' export default function Exhibitions() { // 用状态记录当前激活的标签,初始值设为'current'对应默认显示 const [activeTab, setActiveTab] = useState('current'); return ( <main className="main-exhibitions"> <div className="title"> <div className="title-line-left"></div> <h2>exhibitions</h2> <div className="title-line-right"></div> </div> <div className="exhibitions-navbar"> <span className="nav-past" onClick={() => setActiveTab('past')} >past</span> <span className="nav-current" onClick={() => setActiveTab('current')} >current</span> <span className="nav-future" onClick={() => setActiveTab('future')} >future</span> </div> {/* 根据activeTab状态控制元素显示 */} <div className="ex-past" style={{ display: activeTab === 'past' ? 'block' : 'none' }}> <h4>past</h4> </div> <div className="ex-current" style={{ display: activeTab === 'current' ? 'block' : 'none' }}> <h4>current</h4> </div> <div className="ex-future" style={{ display: activeTab === 'future' ? 'block' : 'none' }}> <span className="future-span">No future exhibitions planned at the moment. Keep an eye on this page for updates!</span> </div> </main> ) }
此时可以直接删除exhibitions-nav.js文件,因为不再需要原生JS处理逻辑。
额外优化(可选)
如果不想用内联样式,也可以通过动态添加/移除类名控制显示:
- 修改CSS,新增一个
active类:
.ex-past, .ex-future { display: none; } .ex-current { display: block; } .ex-tab.active { display: block; }
- 给内容容器统一添加
ex-tab类,根据状态添加active:
// ... 其他代码不变 <div className={`ex-past ex-tab ${activeTab === 'past' ? 'active' : ''}`}> <h4>past</h4> </div> <div className={`ex-current ex-tab ${activeTab === 'current' ? 'active' : ''}`}> <h4>current</h4> </div> <div className={`ex-future ex-tab ${activeTab === 'future' ? 'active' : ''}`}> <span className="future-span">No future exhibitions planned at the moment. Keep an eye on this page for updates!</span> </div>
这种方式更符合CSS管理习惯,便于后续扩展样式。
内容的提问来源于stack exchange,提问作者paparonnie
相关产品推荐
相关产品推荐

