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

My Goal:I'm trying to build a ponent that when you give it props.items and props.fadeEvery, it wi

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.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745609482a4635864.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信