0

I want to loop over an array of 3 months ["1", "2", "3"] and show the current month every 2 seconds. I tried using useEffet, useState and setInterval to build a cycle but i get an unwanted behavior.

Here is a sandbox i made : https://codesandbox.io/s/autumn-voice-y6kdpb?file=/src/App.js

import { useState, useEffect } from "react";
import "./styles.css";

export default function App() {
  const months = ["1", "2", "3"];
  let count = 0;
  const [month, setMonth] = useState(months[count]);
  useEffect(() => {
    function cycleArray() {
      setMonth(months[count]);
      // increment our counter
      count++;
      // reset counter if we reach end of array
    }
    if (count === months.length) {
      count = 0;
    }
    setInterval(cycleArray, 1000);
  }, [count]);
  return (
    <div className="App">
      <h1>Loop over months (next month every 1 second) </h1>
      <h2>{month}</h2>
    </div>
  );
}

What am I doing wrong ?

2
  • 1
    Please add the code to your question Commented Sep 24, 2022 at 0:06
  • 1
    Its important to post the code here instead of providing links as stackoverflow provides a reference to help others. In the future, the link may not work, thus, leaving the future reader without code to reference. Commented Sep 24, 2022 at 0:11

2 Answers 2

3

You should be careful when using things like useInterval inside the useEffect hook. By default the react strict mode is activated in dev mode. This means certain hooks and callbacks are called twice to make the dev aware of not cleaned up side effects inside those hooks and callbacks. In such case your useEffect should return a cleanup function to remove the interval for rerrendering purposes.

import { useState, useEffect } from "react";
import "./styles.css";

export default function App() {
  const months = ["1", "2", "3"];
  const [count, setCount] = useState(0);

  // hook only runs at first render of component and start our interval of 1 sec
  useEffect(() => {
    const interval = setInterval(() => {
      // increment our counter
      setCount((e) => e + 1);     
    }, 1000);

    // our cleanup function, so no interval is left behind when rendering the component new
    return () => {
      clearInterval(interval);
    };
  }, []);

  // our hook that is subscribed to the count state; loops the count back around
  useEffect(() => {
    if (count >= months.length) {
      setCount(0);
    }
  }, [count]);

  return (
    <div className="App">
      <h1>Loop over months (next month every 1 second) </h1>
      <h2>{months[count]}</h2>
    </div>
  );
}

https://codesandbox.io/s/wild-platform-ufegmv?file=/src/App.js

Sign up to request clarification or add additional context in comments.

Comments

-1

It is a good idea to report what "unwanted behavior" is. Just from a quick view, you are using count variable when it isn't necessary. Use the month state variable as the count. But the way you have the code set up, setInterval is unreliable just based on how javascript is written, so letting it run long enough, displaying the months will be irratic.

import { useState, useEffect } from "react";
import "./styles.css";

export default function App() {
  const months = [1, 2, 3];
  const [month, setMonth] = useState(0);
  useEffect(() => {
    function cycleArray() {

      // increment our counter
      setMonth(month + 1);

      // reset counter if we reach end of array
    }
    if (month === months.length) {
      setMonth(0);
    }
    setInterval(cycleArray, 1000);
  }, [month]);
  return (
    <div className="App">
      <h1>Loop over months (next month every 1 second) </h1>
      <h2>{month}</h2>
    </div>
  );
}

1 Comment

This starts an additional interval (without canceling the previous one) every time the month change, so it is not correct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.