Coder Social home page Coder Social logo

Comments (16)

parthnegi21 avatar parthnegi21 commented on October 16, 2024 6

The issue you're encountering relates to React's Strict Mode, which intentionally triggers certain lifecycle methods (including useEffect cleanups) twice to help developers identify potential side effects in their components. This is particularly relevant in React 18 and newer versions.

Explanation
Strict Mode in React 18: When you use StrictMode in React, it intentionally unmounts and remounts components to detect side effects. This means that useEffect cleanups may be triggered more than once, even if the component hasn't been visibly removed from the UI.

Double Rendering: In your example, the first useEffect cleanup (clean counter 0) happens, but before the cleanup is logged, React immediately triggers another render of the component, assigning a new counter value (counter 1). However, the cleanup log shows clean counter 1 because React has already moved on to the next render cycle.

Expected Behavior in Strict Mode
Given the intentional behavior of Strict Mode:

The initial render (counter 0) occurs.
React triggers a cleanup, but since the component re-renders immediately, the counter state increments to 1.
The cleanup log shows clean counter 1 because React is now dealing with the most recent state.
How to Handle It
If you want to observe the cleanup of the first render, you might consider disabling Strict Mode or understanding that this behavior is a part of React's effort to make components more resilient. React's double invocation of certain lifecycle methods (including useEffect cleanups) is designed to catch potential issues in components.

from react.

ivstas avatar ivstas commented on October 16, 2024 1

@gianlucadifrancesco @LoganDark damn guys you were right. The example with counter was misleading.
I rewrote it without global variable and the order is correct: https://playcode.io/1994448

function useSecondsSinceMount() {
  const [seconds, setSeconds] = useState(0);
  const [intervalId, setIntervalId] = useState(null)

  useEffect(() => {
    const id = setInterval(() => {
      setSeconds((current) => current + 1);
    }, 1000);

    console.log("set interval", id);
    setIntervalId(id)

    return () => {
      console.log("clean up interval", id);
      clearInterval(id);
    };
  }, []);

  return [seconds, intervalId];
}

export default function App() {
  const [seconds, intervalId] = useSecondsSinceMount();

  return (
    <div className="App">
      <h2>Seconds since component mount: {seconds}</h2>
      {intervalId && (
        <h3>Used interval: {intervalId}</h3>
      )}
    </div>
  );
}

from react.

ivstas avatar ivstas commented on October 16, 2024

@parthnegi21 did you just put the reported bug into ChatGPT to write this answer?

from react.

parthnegi21 avatar parthnegi21 commented on October 16, 2024

Yes 🥲🥲 new to contribution, didn't know much abut contribution..

from react.

VDXD avatar VDXD commented on October 16, 2024

Hello, so in your code you are using useEffect but i notice that you are not using any clean up function in your useEffect and you are also not using any dependencies array i mean i don't think it any use in that code but use clean up function in your useEffect block and you are also using strict mode which render you app component twice to find any bug and when your code executes for second time the counter stays 1 cause it was 1 in first render,

I hope i didn't complex it out sorry if i did but use clean up function in your useEffect block of code that might fix it

from react.

ivstas avatar ivstas commented on October 16, 2024

@VDXD - the only thing I do in useEffect is return a cleanup function. For dependency array - my fault, thank you for pointing this out. I have fixed the code example in issue as well as playground link.
However, this doesn't change the behavior - it still unmounts the second counter.

from react.

VDXD avatar VDXD commented on October 16, 2024

@ivstas i'm happy that i was able to help you in any way and about return function i thought it was a main function which we give to useEffect sorry about that and i tried lots of solution but nothing seems to works but you can try state in app component and also don't use chatGPT that guy knows nothing about it XD

thank you!

from react.

gianlucadifrancesco avatar gianlucadifrancesco commented on October 16, 2024

@ivstas I think this is because React runs the whole App twice before running the first useEffect, therefore you're calling globalCounter++ twice before the first cleanup is called (consequently, counter is 1 at that point).

I've written a reproduction with some detailed logs, and also added a useEffect in App to more understand the behaviour.

Here's the whole timeline:

  1. Render useCounter (no useEffect) -> returns 0.
  2. Render App (no useEffect) -> current counter 0.
  3. Render useCounter (no useEffect) -> returns 1.
  4. Render App (no useEffect) -> current counter 1.
  5. Run useEffect in useCounter.
  6. Run useEffect in App.
  7. Cleanup useEffect in useCounter -> clean counter 1.
  8. Cleanup useEffect in App -> clean counter 1.
  9. Run useEffect in useCounter.
  10. Run useEffect in App.
  11. (At this point, counter is 1 and globalCounter is 2)

from react.

ivstas avatar ivstas commented on October 16, 2024

@gianlucadifrancesco thanks for such a clean reproduction.
You see, the counter=0 doesn't get clean up, and my point was that it should be cleaned up instead of counter=1

from react.

gianlucadifrancesco avatar gianlucadifrancesco commented on October 16, 2024

@ivstas Yep, it's actually "expected"!
It's discouraged to have a global variable like that in React: that's also one of the things that StrictMode helps to avoid, because you're basically mutating the state outside the scope of React, which doesn't expect that.

In fact, if you remove StrictMode, the rendering result will be different (which is a red flag): current counter 0.

I know it's kinda tricky actually, here's a more detailed explanation in the docs on why it should be avoided.

from react.

ivstas avatar ivstas commented on October 16, 2024

@gianlucadifrancesco

It's discouraged to have a global variable like that in React

I know, but the goal was to show which render is being cleaned-up.
If the idea of <StrictMode> is to enforce developers to maintain cleanup cycle, why the first cleanup is being ommited?

In my original case I had "a disposable context" that was bound to component lifetime.
When I used this context in a child component later, it was already cleaned-up.

from react.

gianlucadifrancesco avatar gianlucadifrancesco commented on October 16, 2024

The cleanup function is still called, but not immediately.
StrictMode unmounts and remounts the components, but doesn't call the cleanup function immediately after unmounting them. What it does is to call the cleanup after the component is mounted twice, and this is done on purpose in order to avoid impure things.

So, on load, instead of this: Mount -> useEffect -> ...
StrictMode does this: Mount -> Mount -> useEffect -> Cleanup -> useEffect -> ...

It basically adds another mount on the first time App is started.
This is done on purpose and, thanks to this, if there's something that React doesn't expect in the code, it's easier to spot it.

But feel free to provide a deeper example, more similar to the disposable context you're talking about! Maybe there's something different going on there.

from react.

LoganDark avatar LoganDark commented on October 16, 2024

It looks like the second render is what's being discarded, the first render's result is then being re-used.

So what's happening is probably:

  • React renders app once
  • React renders app again and then cleans it up
  • React uses the results of the first render

So it's not that the wrong cleanup is being called, it's that a different render is being used than the one that you thought was being used.

Basically, strict mode is not the same as two full renders, it's more like each component's render function is called twice for each one actual render, if that makes any sense.

from react.

ivstas avatar ivstas commented on October 16, 2024

It looks like the second render is what's being discarded, the first render's result is then being re-used

I would be happy if it was true, but unfortunately it's not. It uses the result of second (already cleaned up) render.

from react.

LoganDark avatar LoganDark commented on October 16, 2024

I would be happy if it was true, but unfortunately it's not. It uses the result of second (already cleaned up) render.

image

Oh, it's just running the effect twice.

This is a known property of strict mode, so nothing to worry about.

(Notably, it runs the effect twice without re-rendering App each time, as in it reuses the same callback you provided to useEffect.)

from react.

LoganDark avatar LoganDark commented on October 16, 2024

The only thing that was misleading is that you didn't log when the effect was called but destructor was not called. So you missed the critical detail where the effect is called again after the destructor.

Nothing to do with the counter IMHO.

from react.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.