Estoy tratando de crear un acordeón simple en React. Si abro/actualizo toda la página, el evento onclick no se activa cuando hago clic en los botones. Funciona correctamente solo después de que cambio algo en el código y las representaciones de los componentes. No hay errores en la consola y, sinceramente, no tengo idea de lo que está sucediendo aquí. Gracias por adelantado
import { useEffect } from "react" function FAQ () { const accordionBtn = document.querySelectorAll('.accordionTitle') const allTexts = document.querySelectorAll('.text') useEffect(() => { accordionBtn.forEach(function (el) { el?.addEventListener('click', toggleAccordion) }) }, []) 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') } } 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 FAQCuando recupera los elementos DOM en ese momento, esos elementos ni siquiera están en DOM. Si mueve las llamadas querySelectorAll a useEffect , debería resolver su problema. Recuerde que la devolución de llamada useEffect se llama después de que el componente se haya pintado en la pantalla.
Pero como Andy mencionó en el comentario anterior, no se recomienda enfáticamente acceder al DOM directamente. Incluso si tiene que hacerlo (que no creo que sea necesario en su escenario) hay useRef
Así es como podría abordarlo. Utiliza el estado para mantener el número del acordeón, y luego puede ajustar la clase de los elementos secundarios en consecuencia.
const { useState } = React; function Example() { // Use state to identify the accordian you want open const [ show, setShow ] = useState(0); // `handleClick` grabs the id from the accordian // and then sets the state function handleClick(e) { const { id } = e.target.parentNode.dataset; setShow(Number(id)); } // Now, for each element check to see if the id matches // the state and show that text return ( <div onClick={handleClick}> <div data-id="1"> <h3>Accordian one</h3> <p className={show === 1 && "show"}>Accordian one text</p> </div> <div data-id="2"> <h3>Accordian two</h3> <p className={show === 2 && "show"}>Accordian two text</p> </div> <div data-id="3"> <h3>Accordian three</h3> <p className={show === 3 && "show"}>Accordian three text</p> </div> </div> ); }; // Render it ReactDOM.render( <Example />, document.getElementById("react") ); p { display: none; } h3:hover { cursor: pointer; } .show { display: block; } <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script> <div id="react"></div>Sugeriría: refactorice su código para usar componentes basados en clases. No es que el código existente sea feo, pero debe usar las mejores prácticas cuando se trata de React y renderizar html sin problemas. Además, al hacerlo, también puede usar los métodos React Component Lifecycle, ¡lo cual será muy útil!
Un buen artículo sobre componentes basados en clases: Enlace