Imagina que tengo una serie de objetos como este:
const items = [ {name: "item 0"}, {name: "item 1"}, {name: "item 2"} ]Y un componente como este:
const [currentItemIndex, setCurrentItemIndex] = useState(0) setInterval(function () { if (currentItemIndex < 3) { setCurrentItemIndex(currentItemIndex + 1); } else { clearInterval(); } }, 5000); return ( <div> <p> {items[currentItemIndex].name} <p> </div> ) Quiero actualizar currentItemIndex cada 5 segundos para que el div muestre el siguiente elemento, y si llega al final, debería comenzar de nuevo: 0, 1, 2, 0, 1, 2, 0, 1, 2 ... .
El problema es que se repite correctamente la primera vez, pero después de llegar al final, muestra el primer elemento durante una fracción de segundo y luego pasa al último elemento: 0, 1, 2, 0 (really quick to) 2 .
¿Qué estoy haciendo mal?
Busqué esto, pero todos los artículos hablan de "cómo evitar bucles infinitos", ¡no de "cómo usarlos"!
Puedes hacerlo con useEffect y con setTimeout así:
const items = [{ name: "item 0" }, { name: "item 1" }, { name: "item 2" }]; const App = () => { const [currentItemIndex, setCurrentItemIndex] = React.useState(0); React.useEffect(() => { const id = setTimeout( () => setCurrentItemIndex((currentItemIndex + 1) % items.length), 1000 ); return () => { clearInterval(id); // removes React warning when gets unmounted }; }, [currentItemIndex]); return ( <div> <p> {items[currentItemIndex].name} </p> </div> ); }; // export default App; ReactDOM.render(<App />, document.getElementById('root')); <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="root"></div>Veo algunos problemas con esta lógica que creaste.
En primer lugar, se debe proporcionar a clearInterval una variable que creó al crear la función setInterval (como puede ver aquí ), por lo que clearInterval() no hace nada.
Además de eso, no desea borrar su temporizador cuando currentItemIndex alcanza su umbral, sino que desea que vuelva a cero.
En React, generalmente usamos useEffect para cronometrar eventos, ya que necesita borrarlos al desmontarlos para que no sigan ejecutándose después de desmontar su componente. Algo como esto:
useEffect(() => { const timer = setInterval(yourFunction); return () => clearInterval(timer); }, [])Puede utilizar este enfoque si lo desea. Crea una función que establecerá el estado después de 5 segundos usando el tiempo de espera. Una vez que se establece el estado, vuelva a llamar al mismo método. Por el bien de reaccionar, llame a este método una vez usando useEffect.
import { useEffect, useState } from "react"; const items = [{ name: "item 0" }, { name: "item 1" }, { name: "item 2" }]; export default function App() { const [currentItemIndex, setCurrentItemIndex] = useState(0); useEffect(() => { incrementAfterDelay(1000, 1, 2); }, []); const incrementAfterDelay = (delay, step, lastStep) => { setTimeout( () => setCurrentItemIndex((value) => { return (value < lastStep)? value + step : 0 }, incrementAfterDelay(delay, step, lastStep)), delay ); }; return ( <div> <p> {items[currentItemIndex].name} </p> </div> ); }