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

React手风琴组件addEventListener在首次渲染时不生效问题求助

问题根因

你将document.querySelectorAll逻辑写在了组件函数的顶层,首次渲染时DOM还未挂载到页面,此时查询得到的是空节点集合,useEffect执行时也用的是空集合绑定事件,自然点击无响应。
修改代码触发热重载时,组件重新执行顶层逻辑,此时页面已有已渲染的DOM节点,querySelector能正常拿到元素,事件绑定才生效。

修复方案1:最小改动适配现有代码

把DOM查询逻辑移到useEffect内部,同时补充卸载时的事件移除逻辑避免内存泄漏:

import { useEffect } from "react"

function FAQ () {
  useEffect(() => {
    // DOM查询移到useEffect内部,保证DOM已挂载后再查询
    const accordionBtn = document.querySelectorAll('.accordionTitle')
    const allTexts = document.querySelectorAll('.text')

    function toggleAccordion (el: Event) {
      const targetText = (el?.currentTarget as Element).nextElementSibling?.classList
      const target = (el?.currentTarget as Element).classList

      if (!(targetText?.contains('show'))) {
        accordionBtn.forEach(function (el) {
          el.classList.remove('accordionTitleActive')
          allTexts.forEach(function (el) {
            el.classList.remove('show')
          })
        })
        targetText?.add('show')
        target?.add('accordionTitleActive')
      }
    }

    accordionBtn.forEach(function (el) {
      el?.addEventListener('click', toggleAccordion)
    })

    // 清理函数:组件卸载时移除事件监听
    return () => {
      accordionBtn.forEach(function (el) {
        el?.removeEventListener('click', toggleAccordion)
      })
    }
  }, [])
 
  return (
    <div>
      <ul className='accordion'>
        <li>
          <h2 className='accordionTitle'>Title1 </h2>
          <div className='text show'>
            Content1
          </div>
        </li>
        <li>
          <h2 className='accordionTitle'>Title2 </h2>
          <div className='text'>
            Content2
          </div>
        </li>
        <li>
          <h2 className='accordionTitle'>Title3 </h2>
          <div className='text'>
           Content3
          </div>
        </li>
      </ul>
    </div>
  )
}

export default FAQ

修复方案2:更符合React规范的实现(推荐)

React不推荐直接操作DOM、手动绑定事件,改用状态控制类名切换即可,代码更易维护也不会出现挂载时序问题:

import { useState } from "react"

// 提前定义手风琴数据,方便后续扩展
const faqList = [
  { title: 'Title1', content: 'Content1' },
  { title: 'Title2', content: 'Content2' },
  { title: 'Title3', content: 'Content3' },
]

function FAQ () {
  // 用状态存储当前激活的索引,默认激活第一个
  const [activeIndex, setActiveIndex] = useState(0)

  return (
    <div>
      <ul className='accordion'>
        {faqList.map((item, index) => (
          <li key={index}>
            <h2 
              className={`accordionTitle ${activeIndex === index ? 'accordionTitleActive' : ''}`}
              onClick={() => setActiveIndex(index)}
            >
              {item.title}
            </h2>
            <div className={`text ${activeIndex === index ? 'show' : ''}`}>
              {item.content}
            </div>
          </li>
        ))}
      </ul>
    </div>
  )
}

export default FAQ

内容的提问来源于stack exchange,提问作者Johnny92

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 13:45:03