Estoy tratando de acceder a la barra de desplazamiento horizontal con botones en reaccionar, pero resulta que no funciona debido a un error que no puede leer los valores nulos de .scrollLeft .
<> <div className="containerOuterSider"> <FaAngleLeft className= "FaAngleLeft" onClick={slide('left')}/> <div id="container3" className="container3"> {products && products .map((product) => ( <AAsOfLowNav key={product._id} product={product} /> )) .reverse()} </div> <FaAngleRight className="FaAngleRight" onClick={slide('right')}/> </div> </>y la función principal es
function slide(direction) { var container = document.getElementById('container3'); let scrollCompleted = 0; var slideVar = setInterval(function() { if (direction === 'left') { container.scrollLeft -= 10; } else { container.scrollLeft += 10; } scrollCompleted += 10; if (scrollCompleted >= 100) { window.clearInterval(slideVar); } }, 50); }El error al que me enfrento es:
TypeError no detectado: no se pueden leer las propiedades de nulo (leyendo 'scrollLeft') en LowerCatNav.js:31:1 TypeError no detectado: no se pueden leer las propiedades de nulo (leyendo 'scrollLeft') en LowerCatNav.js:33:1
¿Cómo puedo hacerlo bien? ¿Debo usar los ganchos o algo más?
Inmediatamente está invocando la función slide mientras renderiza:
<FaAngleLeft className="FaAngleLeft" onClick={slide('left')} // <-- immediately invoked when rendered /> ... <FaAngleRight className="FaAngleRight" onClick={slide('right')} // <-- immediately invoked when rendered /> El componente React no se ha renderizado completamente ni se ha enviado al DOM, por lo que las consultas al DOM, es decir, document.getElementById('container3') , devuelven null .
Corrija el controlador de clics para que slide no se invoque de inmediato.
<FaAngleLeft className="FaAngleLeft" onClick={() => slide('left')} // <-- asynchronously invoked when clicked /> ... <FaAngleRight className="FaAngleRight" onClick={() => slide('right')} // <-- asynchronously invoked when clicked />Se considera un antipatrón React para consultar directamente el DOM para DOMNodes, use una referencia React para esto.
Ejemplo:
... const containerRef = React.useRef(); // <-- (1) create Ref const sliderTimerRef = React.useRef(); useEffect(() => { return () => { // clear any running intervals on component unmount clearInterval(sliderTimerRef.current); }; }, []); ... function slide(direction) { // clear any previously set intervals and reset scrollCompleted clearInterval(sliderTimerRef.current); let scrollCompleted = 0; sliderTimerRef.current = setInterval(function() { const container = containerRef.current; // <-- (3) access current ref value if (direction === 'left') { container?.scrollLeft -= 10; // <-- (4) Optional Chaining null check } else { container?.scrollLeft += 10; // <-- (4) Optional Chaining null check } scrollCompleted += 10; if (scrollCompleted >= 100) { clearInterval(sliderTimerRef.current); } }, 50); } ... return ( <> <div className="containerOuterSider"> <FaAngleLeft className="FaAngleLeft" onClick={() => slide('left')}/> <div ref={containerRef} // <-- (2) attach ref to element id="container3" className="container3" > {products .map((product) => ( <AAsOfLowNav key={product._id} product={product} /> )) .reverse() } </div> <FaAngleRight className="FaAngleRight" onClick={() => slide('right')}/> </div> </> );slide se llama cuando #container3 aún no se ha creado.
Cambie onClick={slide(...)} a onClick={() => slide(...)}
Debe evitarse el acceso a elementos globales (por ejemplo, por id).
Prefiera ref / createRef / useRef para acceder a los elementos que pertenecen a su componente.