FAQ: The Effect Hook - Control When Effects Are Called

This community-built FAQ covers the “Control When Effects Are Called” exercise from the lesson “The Effect Hook”.

Paths and Courses
This exercise can be found in the following Codecademy content:

Learn React

FAQs on the exercise Control When Effects Are Called

There are currently no frequently asked questions associated with this exercise – that’s where you come in! You can contribute to this section by offering your own questions, answers, or clarifications on this exercise. Ask or answer a question by clicking reply (reply) below.

If you’ve had an “aha” moment about the concepts, formatting, syntax, or anything else with this exercise, consider sharing those insights! Teaching others and answering their questions is one of the best ways to learn and stay sharp.

Join the Discussion. Help a fellow learner on their journey.

Ask or answer a question about this exercise by clicking reply (reply) below!
You can also find further discussion and get answers to your questions over in Language Help.

Agree with a comment or answer? Like (like) to up-vote the contribution!

Need broader help or resources? Head to Language Help and Tips and Resources. If you are wanting feedback or inspiration for a project, check out Projects.

Looking for motivation to keep learning? Join our wider discussions in Community

Learn more about how to use this guide.

Found a bug? Report it online, or post in Bug Reporting

Have a question about your account or billing? Reach out to our customer support team!

None of the above? Find out where to ask other questions here!

Once you has used the empty dependency array as the second argument in the useEffect(), the return statement is ever called when the Component unmounts?

I can’t see that behaviour if I refresh the window.

useEffect(() => {
    const intervalId = setInterval(() => {
      setTime((prev) => prev + 1);
    }, 1000);
    return () => {
      clearInterval(intervalId);
    }
}, []);
1 Like

Yeah I may be wrong, but it seems like in this case, the empty dependency array is achieving the same effect as the ‘return’ statement. If I comment out the ‘return’ statement and keep the empty dependency array and refresh, the behavior remains the same.

2 Likes

I’m not sure if i’m the only one having this issue. But it doesn’t look like the return statement seems to clear the interval after step 2. The count still increases very quickly. Anyone else having this issue?

export default function Timer() {
  const [time, setTime] = useState(0);

  useEffect(()=>{
    const intervalId = setInterval(()=> {
      setTime((prev)=> prev+1)
    }, 1000)
  };
  return ()=> {clearInterval(intervalId)}
  )

You have placed the cleanup function outside the function called by useEffect. Have a look at the previous lesson “Clean Up Effects” to see where the cleanup function should be positioned.

The above snippet should be edited to:

useEffect(() => {
    const intervalId = setInterval(() => {
      setTime((prev) => prev + 1);
    }, 1000);
    return () => {
      clearInterval(intervalId);
    };
  });

Got it, that fixed it. Thanks Faraz! Been a little unclear on where to place brackets. But looks like useEffects first parameter looks for a function that contains the effect function, then after a semicolon (within the main function of the first parameter) if it finds another function it identifies it as the clean up function. That sound right?

As I understand it, the first argument to the useEffect is a callback function, while the second optional argument is an array of dependencies (we separate the two arguments by a comma).
So, the signature is useEffect(callback function, optional dependency array)
Within our callback function, we can implement whatever effects we want to happen. In the snippet you posted, we are doing just one effect involving the interval. If we wanted, we could do a series of effects. It is a good idea to separate the statements by semicolons. So, the basic structure would be something like:

useEffect(() => {
  effect 1;
  effect 2;
  effect 3;
}
)

While we can carry out multiple effects in a single callback function, the function can only have one return statement which should be placed at the bottom of the function. If we don’t want any cleanup function, then we can omit the return statement. If we do want a cleanup function, then the return statement should be placed at the bottom of the callback function. If we place the return statement earlier, then any effects after the return statement would be lost. We can only return one cleanup function, though within the cleanup function we can do multiple cleanups.
So, the structure is:

useEffect(() => {
  effect 1;
  effect 2;
  effect 3;
  return cleanup function;  // If we don't need to cleanup, 
                           //then we can omit return statement
}
)

useEffect understands that if the callback function is returning some function, then that returned function should be treated as a cleanup function.

4 Likes

The timer does not update even when using the solution code. I’m using Firefox.

Could you copy paste your code or a screenshot of the whole code? Perhaps that may offer any clues as to why the timer is not working.

Per this lession, “If we want to only call our effect after the first render, we pass an empty array to useEffect() as the second argument.”
I am wondering how is the “time” state updating every second even after the first render if we have passed an empty array to useEffect(). Shouldn’t timer stop at “1” because after “1”, component re-renders, but useEffect() is not called due to empty dependency array?

From the description of setInterval():

The setInterval() method calls a function or evaluates an expression at specified intervals (in milliseconds).

The setInterval() method will continue calling the function until clearInterval() is called, or the window is closed.

So, after the first render, setInterval() creates an interval and then after our specified interval of 1 second, it keeps calling our function (our arrow function with setTime) after every second. Since we passed an empty dependency array, our useEffect will not be called after every re-render. So, we won’t end up with new intervals being created. But the original interval is still running and will keep on running until we close the window or clear the interval.

Hi , I have ticked all my tasks in the exercise but I cant see the textbox, its not getting rendered .
Following is my code which im pretty sure will be same for everybody

import React, { useState,useEffect } from 'react';

export default function Timer() {
  const [time, setTime] = useState(0);
  const [name, setName] = useState("");

useEffect(()=>{
  const intervalId = setInterval(()=>{
      setTime((prev) => prev + 1)
  },1000);
}
return () => {
  clearInterval(intervalId);
}
,[]);

  const handleChange = ({ target }) => setName(target.value);

  return (
    <>
      <input value={name} onChange={handleChange} type='text' />
      <h1>Time: {time}</h1>
    
    </>
  );
}



If you scroll up the thread a bit, you will see that your issue is similar to FAQ: The Effect Hook - Control When Effects Are Called - #5 by edwinrosemond1

Basically, the signature of the useEffect is:
useEffect(callback function, optional dependency array)
The return cleanup function is to be placed at the end of the callback function but still within the body of the callback function.
In the snippet you posted, you have tried to use the signature:
useEffect(callback function, cleanup function, optional dependency array)
which isn’t correct.

Your snippet should be modified to:

useEffect(()=>{
  const intervalId = setInterval(()=>{
      setTime((prev) => prev + 1)
  },1000);
/* DON'T CLOSE THE CALLBACK FUNCTION YET */
return () => {
  clearInterval(intervalId);
}
} ,[]);        /* <---- END THE CALLBACK FUNCTION HERE */
1 Like

I don’t understand why there are no curly brackets around the body of the ‘setTime’'s argument function.
For clarity, in “setTime((prev) => prev + 1);” Why no curly brackets around “prev + 1” ?

From:

useEffect(() => {
    const intervalId = setInterval(() => {
      setTime((prev) => prev + 1);
    }, 1000);
    return () => {
      clearInterval(intervalId);
    }
}, []);

It is just a concise way of writing it. It has to do with implicit and explicit returns in an arrow function.
If you want to write it with the curly braces, then you must explicitly return whatever value you wish to return. So, with the curly braces you would have to write it as:
setTime((prev) => { return prev + 1; });

If you omit the curly braces and write it in concise form as
setTime((prev) => prev + 1; );
then you don’t need to explicitly use the return keyword. The value to the right of the arrow, i.e. prev +1 in this specific case, will be implicitly returned.

Have a look at this article. It is a short read, but it explains the issue very nicely with examples.
https://medium.com/@bunlong/arrow-functions-return-rules-in-javascript-b63ed5f25994

3 Likes

Another day, another buggy lesson.

  1. If you store setInterval() in another variable name…
useEffect(() => {

    const intervalTime = setInterval(() => {

      setTime((prevTime) => prevTime + 1)

    }, 1000);

    return () => {

      clearInterval(intervalTime);

    }

  }, []);

…you get an error after setting clearInterval() saying that you should change it to intervalId while nowhere in the steps, you are told to do so.

  1. On step three if you define the function in this way:

const handleChange = (event) => setName(event.target.value);

…you get an error (although the above declaration is absolutely correct) saying this:

image

Step three also has this confusing step:

  • Add an input tag to our JSX whose value is managed by a current state variable called name with a state setter called setName() .
  • Define an event handler named handleChange() and call that event handler with the input’s onChange event listener attribute.

This greatly confuses learners since the first sentence talks about adding an input tag, then talks about the inside of handleChange() and then in the second sentence it starts talking about that handleChange(). (So confusing that it’s even difficult to describe the problem!)

  1. The JSX part is defined like this:
return (

    <>

      <h1>Time: {time}</h1>

      <input value={name} onChange={handleChange} type='text' />

    </>

  );

No input field was shown for me on screen but after I changed <> </> to <div> </div> it showed the input field.

I don’t know what has happened but these lessons about Hooks are so poorly designed and buggy that instead of wanting to learn you just wanna get it over with.

6 Likes

Are you saying there are no curly brackets allowed? Because I just reported a similar bug where the parentheses are required to pass the lesson, but are not required syntax. I wrote:

setTime(prev => prev + 1)

And it was rejected until I wrapped the argument in parentheses. Valid syntax and it was actually working fine without it.

1 Like

if you’ve forgot the type=“text” attribute, CodeAcademy gives you a wrong error :wink:
So you have to look into the hint. Could be improved :slight_smile:

1 Like

Hi, I used the following code and the time started counting the correct way right away, so I’m kind of puzzled whether this would be a correct solution to the exercise.

import React, { useState, useEffect } from 'react';

export default function Timer() {
  const [time, setTime] = useState(0);

  useEffect(() => {
     setInterval( () =>
       setTime( (prev) => {
            return prev +1
         }
       ), 1000);
  }, []
  )

  return (
    <>
      <h1>Time: {time}</h1>
    </>
  );
}

Also, I’m now stuck because the assignment still wants me to write some different code. I don’t understand why I need all this additional code if my solution works, too?!

I noticed all that too. In my own case, I used “intervalID” and it returned an error.

1 Like