最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React: setInterval Not working after one interval - Stack Overflow

programmeradmin1浏览0评论

My Goal:

I'm trying to build a ponent that when you give it props.items and props.fadeEvery, it will act as a text rotator. I eventually want it to fade in an out, but I'm having trouble with my window.setInterval.

Possible Issue:

I'm calling setIndex in the useEffect hook, but is that not good practice? How an I have it iterate through the array items infinitely?

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;

My Goal:

I'm trying to build a ponent that when you give it props.items and props.fadeEvery, it will act as a text rotator. I eventually want it to fade in an out, but I'm having trouble with my window.setInterval.

Possible Issue:

I'm calling setIndex in the useEffect hook, but is that not good practice? How an I have it iterate through the array items infinitely?

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;
Share Improve this question asked Apr 7, 2021 at 20:49 jefelewisjefelewis 2,0592 gold badges34 silver badges68 bronze badges 3
  • 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 – Keith Commented Apr 7, 2021 at 20:54
  • 1 Just another heads up, if your using setInterval, it's most likely better to clearInterval not clearTimeout. MDN does say the ID's are shared, but for clarity its best to keep them in sync. – Keith Commented Apr 7, 2021 at 21:11
  • Does this answer your question? State not updating when using React state hook within setInterval – Yangshun Tay Commented Dec 17, 2022 at 0:42
Add a ment  | 

3 Answers 3

Reset to default 4

Your index values are taken from initital closure and it won't update unless useEffect callback is called again. You can instead use functional way to update state

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);
  }, []);

Below I've knocked up a snippet using the callback version of setState, this avoid the closure issue you get by using useEffect with []..

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./react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg./react-dom@17/umd/react-dom.development.js"></script>

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

As @Keith said:

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

So, your useEffect hook will be:

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]);

Here is the working demo at CodeSandbox.

发布评论

评论列表(0)

  1. 暂无评论