Как отобразить массив и изменить асинхронность состояния с помощью ответных хуков - PullRequest
0 голосов
/ 28 октября 2019

Я хочу, чтобы компонент перерисовывал каждые 5 секунд и отображал массив по соответствующим индексам, длина массива точно равна 10.

вот что я сделал до сих пор:

const GameCard = ({ gameArray, startGame }) => {

  const [arrayIndex, setArrayIndex] = useState(0);
  let {questionWord} = gameArray[arrayIndex] ;

  useEffect(() => {

      if(!startGame) return;

      let timer = setTimeout(() => {
        if(arrayIndex === 9) return; //is this valid ?
        setArrayIndex(arrayIndex +1)} , 1500)

      return () => {
          clearTimeout(timer)
      }
  }, [arrayIndex ,startGame]);

  return (<div>{questionWord}</div>)

startGame - это булево свойство, которое изменяется при щелчке пользователя.

Теперь это работает, но, как вы можете видеть, я запускаю базу useEffect для 2 переменных, и когда я достигаю концамассива, который я возвращаю в setTimeout, препятствуя обновлению arrayIndex.

Это кажется хакерским, как я могу улучшить свой асинхронный useEffect? и что происходит, когда мы возвращаемся из функции setTimeout или используемEffect?

1 Ответ

1 голос
/ 28 октября 2019

Может быть соблазнительно использовать функциональные обновления , например setArrayIndex((i) => i + 1):

Если новое состояние вычисляется с использованием предыдущего состояния, вы можете передать функцию setState.

Однако ваш код должен использовать исходное значение константы arrayIndex, т. Е. Если оно было 0 к моменту регистрации setTimeout, вы хотите, чтобы оно было 0даже через 5 секунд.

Это уже есть в вашем коде - он будет отличаться в разных рендерах (каждый таймер будет иметь разное значение, потому что он был зарегистрирован в другом рендере), но значение никогда не изменитсямежду регистрацией и выполнением таймера (см. Закрытия ).


Что касается улучшений, то можно обнаружить arrayIndex === 9 даже до регистрации нового setTimeout, например:
useEffect(() => {
  if(!startGame || arrayIndex >= 9) return

  const timer = setTimeout(() => {
    setArrayIndex(arrayIndex + 1)
  }, 5000)
  return () => clearTimeout(timer)
}, [arrayIndex, startGame])

Более того, если вы хотите, чтобы таймер сбрасывал время рендеринга (т.е. не 5секунд после каждого рендеринга, но с интервалом в 5 секунд) вам понадобится изменяемая ссылка вместо неизменяемого состояния:

const arrayIndexRef = useRef(0)
const arrayIndex = arrayIndexRef.current
useEffect(() => {
  if(!startGame) return

  const timer = setInterval(() => {
    arrayIndexRef.current += 1
    if (arrayIndexRef.current >= 9) {
      clearInterval(timer)
    }
  }, 5000)
  return () => clearInterval(timer)
}, [startGame])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...