0

I'm learning the useEffect; I'm running into an exercise with a non-empty independency array since I'm passing the index of the array.

The problem is that I get same name repeated.

This is my code and below is the result.

let users = ['Oliver', 'Thomas', 'George', 'George', 'William']
export default function App() {
  const [index, setIndex] = useState(0);
  console.log('RENDER');
  useEffect(() => {
    console.log('Hello ' + users[index]);
    console.log('Side Effect RUNS!');
    setTimeout(() => setIndex(index => index + 1), 1000)
    if (index === users.length - 1) {
      return
    }
  }, [index])
}

RESULT:

RENDER

Hello Oliver

Side Effect RUNS!

RENDER

Hello Thomas

Side Effect RUNS!

RENDER

Hello George

Side Effect RUNS!

RENDER

Hello George

Side Effect RUNS!

RENDER

Hello William

Side Effect RUNS!

RENDER

Hello undefined

Side Effect RUNS!

RENDER

Hello undefined

Side Effect RUNS!

RENDER

Hello undefined

Side Effect RUNS!

Also as you can see, I get undefined.

How can I solve the problem?

Sarah
  • 292
  • 1
  • 10
  • `useEffect` render twice in only in development not in Production. You can see your problem solution on react official doc https://react.dev/reference/react/useEffect#my-effect-runs-twice-when-the-component-mounts – sahilatahar Aug 14 '23 at 14:59
  • So how could I fix the problem? Can you give me an example please to better understand? – Sarah Aug 14 '23 at 15:04
  • Checkout this post https://stackoverflow.com/questions/53120972/how-to-call-loading-function-with-react-useeffect-only-once – sahilatahar Aug 14 '23 at 15:06

3 Answers3

0

It will run and print each user value and will not print undefined and will not render same output:

import { useState, useEffect } from "react";

let users = ['Oliver', 'Thomas', 'George', 'George', 'William'];

export default function App() {
  const [index, setIndex] = useState(0);

  console.log('RENDER');
  useEffect(() => {
    console.log('Hello ' + users[index]);
    console.log('Side Effect RUNS!');

    if (index < users.length - 1) {
      let timeOut = setTimeout(() => {
        setIndex(preIndex => preIndex + 1);
      }, 1000);

      return () => {
        clearTimeout(timeOut); // Clean up the timeout on component unmount or re-render
      };
    }
  }, [index]);

  return (
    <div>
      <p>Index: {index}</p>
    </div>
  );
}
sahilatahar
  • 568
  • 2
  • 13
0

Try the following solution, which will make it possible for you to reiterate the list of users without displaying undefined:

let users = ['Oliver', 'Thomas', 'George', 'George', 'William']
export default function App() {
  const [index, setIndex] = useState(0);
  console.log('RENDER');
  useEffect(() => {
    console.log('Hello ' + users[index]);
    console.log('Side Effect RUNS!');
    setTimeout(
      () =>
      setIndex((index) => {
        if (index + 1 > users.length - 1) {
          return 0;
        }

        return index + 1;
      }),
      1000
    );
  }, [index])
}
0

Callback inside useEffect gets called every time a dependency changes, this callback will change index every time it runs and changing index will re-render component, which will cause the callback inside useEffect to re-run again and ofcourse it's a infinite loop. Returning from the function when index is last, won't change anything, that's gonna break the current call but it won't stop from calling callback in the next render which is bound to happen. You can add a condition when setting a timeout, to only change the value of index when there's item next to it.

let users = ['Oliver', 'Thomas', 'George', 'George', 'William']
export default function App() {
  const [index, setIndex] = useState(0);
  console.log('RENDER');
  useEffect(() => {
    console.log('Hello ' + users[index]);
    console.log('Side Effect RUNS!');
    setTimeout(() => {
      if (users[index + 1] !== undefined) 
        setIndex(index => index + 1)
    }, 1000)
  }, [index])
}
  • Can you give me an example to better understand please? – Sarah Aug 14 '23 at 15:26
  • I added a condition to only increment index when there's item in the list on next index. This is going to stop iterating once a list is completed. – Rao Zaeem Shahid Aug 14 '23 at 15:39
  • from what i understand does useEffect only work with a component render? because looking at this example https://codesandbox.io/s/nifty-yonath-mo2qf?file=/src/Greet.js it works differently from mine – Sarah Aug 14 '23 at 15:58
  • In the example you mentioned, the condition to break by return statement is before setTImeout. While in your code, it's checking the condition and returning from callback after setting setTimeout. – Rao Zaeem Shahid Aug 14 '23 at 17:33