Estaba creando una interfaz de usuario para un diccionario que muestra una palabra seguida de su significado. La palabra cambia a través de un setInterval . En el siguiente código, estoy configurando setInterval inicialmente y luego en algunos eventos. Aquí solo estoy enfocado en el setInterval inicial.
Aunque tengo bastante experiencia en React, todavía no puedo entender por qué setInterval funciona cuando está dentro del gancho useEffect pero no desde la condición if.
Para replicar simplemente comente la condición if y elimine el comentario del bloque useEffect .
¿Puede alguien por favor explicar?
import React, { useState, useEffect } from 'react'; import Dictionary from './Dictionary.json'; import './App.css'; const words = Object.keys(Dictionary); let currentInterval; function App() { const [randomWord, setRandomWord] = useState(words && words[Math.round(Math.random() * words.length)]); const [wordGenerationDuration, setWordGenerationDuration] = useState(10000); function selectRandomWord() { setRandomWord(words[Math.round(Math.random() * (words.length - 1))]); } if(!currentInterval) { currentInterval = setInterval(selectRandomWord, wordGenerationDuration); } // useEffect( // () => { // currentInterval = setInterval(selectRandomWord, wordGenerationDuration); // }, [] // ) return ( <div className='App'> <div className='dictionary-wrapper'> <p className='dictionary-word'> {randomWord} </p> <p className='dictionary-meaning'> {Dictionary[randomWord]} </p> <p className='next-button cursor-pointer'> <span onClick={ () => { clearInterval(currentInterval); selectRandomWord(); currentInterval = setInterval(selectRandomWord, wordGenerationDuration); } } > Next Word </span> </p> <p> <span>Auto Switch Duration:</span> <input className='dictionary-input' type='number' placeholder={wordGenerationDuration / 1000} onChange={ (evt) => { const wordGenerationDuration = Math.max(evt.target?.value * 1000, 10000); clearInterval(currentInterval); setWordGenerationDuration(wordGenerationDuration); currentInterval = setInterval(selectRandomWord, wordGenerationDuration); } } min='10' /> </p> </div> </div> ); } export default App;Su devolución de llamada de intervalo original es una función definida dentro de su componente ( selectRandomWord ).
Cuando su componente se vuelve a renderizar, el currentInterval se establecerá para que su intervalo no se redefina. El intervalo existente se referirá entonces a la versión de renderizado anterior de selectRandomWord que ya no actualiza el estado del renderizado actual.
Lo que realmente deberías hacer es
useEffect() , y function App() { const interval = useRef() const [randomWord, setRandomWord] = useState(words && words[Math.round(Math.random() * words.length)]); const [wordGenerationDuration, setWordGenerationDuration] = useState(10000); function selectRandomWord() { setRandomWord(words[Math.round(Math.random() * (words.length - 1))]); } useEffect(() => { if (!interval.current) { interval.current = setInterval(selectRandomWord, wordGenerationDuration) } // cleanup return () => { clearInterval(interval.current) } }, [])También puede borrar el intervalo en cualquier momento usando
clearInterval(interval.current)Ver también https://reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables