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

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处理逻辑。

额外优化(可选)

如果不想用内联样式,也可以通过动态添加/移除类名控制显示:

  1. 修改CSS,新增一个active类:
.ex-past, .ex-future {
    display: none;
}

.ex-current {
    display: block;
}

.ex-tab.active {
    display: block;
}
  1. 给内容容器统一添加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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 07:24:18