Реагировать: setInterval Не работает через один интервал

Моя цель:

Я пытаюсь создать компонент, который, когда вы дадите ему props.items и props.fadeEvery, он будет действовать как ротатор текста. В конце концов я хочу, чтобы он постепенно исчезал, но у меня проблемы с моим window.setInterval.

Возможная проблема:

Я вызываю setIndex в ловушке useEffect, но разве это не хорошая практика? Как я могу бесконечно перебирать элементы массива?

TextFade.tsx

// Imports: Dependencies
import React, { useState, useEffect } from 'react';

// TypeScript Type: Props
interface Props {
  items: Array<string>,
  fadeEvery: number,
};

// Component: Text Fade
const TextFade: React.FC<Props> = (props): JSX.Element => {
  // React Hooks: State
  const [ index, setIndex ] = useState<number>(0);

  // React Hooks: Lifecycle Methods
  useEffect(() => {
    const timeoutID: number = window.setInterval(() => {
      // End Of Array
      if (index > props.items.length) {
        // Set Data
        setIndex(0);
      }
      else {
        // Set Data
        setIndex(index + 1);
      }
    }, props.fadeEvery * 1000);

    // Clear Timeout On Component Unmount
    return () => window.clearTimeout(timeoutID);
  }, []);

  return (
    <div id = "text-fade-container">
      <p id = "text-fade-text">{props.items[index]}</p>
    </div>
  );
};

// Exports
export default TextFade;
index находится в так называемом закрытии. Ваш эффект использования должен отображаться только один раз [] .. Поэтому вам нужно использовать версию обратного вызова setIndex.
Keith 07.04.2021 23:54

Еще одно предупреждение: если вы используете setInterval, то, скорее всего, лучше использовать clearInterval, а не clearTimeout. MDN действительно говорит, что идентификаторы являются общими, но для ясности лучше синхронизировать их.

Keith 08.04.2021 00:11
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
2
42
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Значения вашего индекса берутся из начального закрытия, и он не будет обновляться, пока не будет снова вызван обратный вызов useEffect. Вместо этого вы можете использовать функциональный способ обновления состояния

useEffect(() => {
    const timeoutID: number = window.setInterval(() => {
      // End Of Array 
      setIndex(prevIdx => {
         if (prevIdx > props.items.length) {
            // Set Data
             return 0;
         }
         else {
           // Set Data
           return prevIdx + 1;
          }
      })
      
    }, props.fadeEvery * 1000);

    // Clear Timeout On Component Unmount
    return () => window.clearTimeout(timeoutID);
  }, []);

Отлично, что сработало! Спасибо за помощь!

jefelewis 08.04.2021 00:05

Как сказал @Keith:

index is in what's called a closure. Your use effect has been told to only render once [].. So you need to use the callback version of setIndex

Итак, ваш хук useEffect будет:

useEffect(() => {
    const timeoutID: number = window.setInterval(() => {
      // End Of Array
      if (index > props.items.length) {
        // Set Data
        setIndex(0);
      } else {
        // Set Data
        setIndex(index + 1);
      }
    }, props.fadeEvery * 1000);

    // Clear Timeout On Component Unmount
    return () => window.clearTimeout(timeoutID);
  }, [index]);

Вот рабочая демонстрация на CodeSandbox.

Не полагайтесь на оставшиеся комментарии. Их часто снимают.

Heretic Monkey 08.04.2021 00:05

ОК. Спасибо, @HereticMonkey.

Zunayed Shahriar 08.04.2021 00:07

Здесь используется зависимость useEffect, а не обратный вызов setState, и это, безусловно, еще один вариант. Вы могли бы даже тогда заменить setInterval на setTimeout. Если бы использование useEffect могло занять много времени, я бы даже сказал, что это лучший вариант.

Keith 08.04.2021 00:22

Ты прав. Спасибо, @Keith.

Zunayed Shahriar 08.04.2021 00:24

Ниже я создал фрагмент, использующий версию обратного вызова setState, это позволяет избежать проблемы с закрытием, возникающей при использовании useEffect с [] ..

const {useState, useEffect} = React;


const TextFade = (props) => {
  // React Hooks: State
  const [ index, setIndex ] = useState(0);

  // React Hooks: Lifecycle Methods
  useEffect(() => {
    const timeoutID: number = window.setInterval(() => {
      // End Of Array
      setIndex(index => 
        index + 1 >= props.items.length
          ? 0
          : index + 1);
    }, props.fadeEvery * 1000);

    // Clear Timeout On Component Unmount
    return () => window.clearInterval(timeoutID);
  }, []);

  return (
    <div id = "text-fade-container">
      <p id = "text-fade-text">{props.items[index]}</p>
    </div>
  );
};



ReactDOM.render(<TextFade items = {['one','two', 'three']} fadeEvery = {1}/>, document.querySelector('#mount'));
<script crossorigin src = "https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src = "https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>

<div id = "mount"></div>

Другие вопросы по теме