Several years ago, I wrote an article about how to create constructor-like functionality in React with function-based components. (You can read it...
For further actions, you may consider blocking this person and/or reporting abuse
I always use:
useMemo(()=>doThisOnceRightNow(), [])
Oooh, I like this.
BTW, I even added this at the end of the article as the best solution (and credited you).
But this is wrong.
useMemo is a cache layer, no guaranty your useMemo will not be trigerred twice if the cache get evicted (which it does if you use suspended components or react 18 concurrent rendering).
Check useMemo docs about useMemo and suspend :
beta.reactjs.org/reference/react/u...
And also this issue that confirm this behaviour: github.com/facebook/react/issues/1...
OK, correct me if I'm wrong (and I very well might be), but it seems that both of the links you provide ultimately resolve to the issue of these items being called twice in development mode???
No, read carrefully :
OK, but...
(And I'm not trying to be resistant here - I'm honestly interested in understanding this process as well as I can.) What exactly makes makes the component "suspend" during the initial mount? And if it did suspend, then doesn't it make logical sense that you would want that function to be called again when it re-mounts??
Specifically, it says, "This should match your expectations if you rely on useMemo solely as a performance optimization."
But this is (sorta) a "performance optimization". Yes, there's a procedural aspect to it as well (as in, I want this logic to be called before the render), but there's also a performance aspect (as in, I only want this logic to be called once for the entire lifecycle of the component).
using suspense + throw a promise.
Maybe yes. Depends on what you wanted to do on construction. but if no side effects (no api call, no logging, etc) you're of course good to execute it twice.
So still be carreful, because they say this :
This means, cache can be evicted in the future without even unmounting.
I wanted to higlight a potential unexpected behaviour like the one reported on the issue.
So me saying "this is wrong" was a bit too harsh. A better answer would have been: "be aware of the caveats, it's not bullet proof".
Ohhhh, I totally get it. And I appreciate it. I've been doing React dev now for 6 years and sometimes I get to feeling like I know it all. And then... I'm slapped back into reality.
So I definitely appreciate the clarifications. Yeah... "this is wrong" may have been an overly-strong retort. But it's good as an impetus to do a "deeper dive".
Thanks!
Agreed, given that throwing an exception will unwind the state I expected that all state stored things go away (as your link seems to confirm), given that the useState initializer and the useMemo get re-run I'm presuming that the useRefs also get a new state - do you know if they do? Effectively Suspense is catching an expectionhigher up - to my understanding, the component is obliterated and is going to be initialised again in that case.
Suspense boundary within the component or an awareness that suspending causes remounting are the ways perhaps?
That's basically the React Hooks version of an IIFE singleton.
Hello adam,
Nice tip, i would add some more :
useLayoutEffect
don't answer your need first.useState
initial function alternative :and use it like this :
This is essentially what I did in my previous approach with
useConstructor()
. Granted, in my original approach, I used a state variable as the tracking variable. But I've since updated it to use a ref.Also, although I like
useLayoutEffect()
, I'm resistant to use it much because it delays display features.Hi and thank you for your article!
What is the main benefit of using this technique over using a
useEffect
?I believe you could have a code that runs when the component gets initialized using this piece of code.
useEffect()
is the out-of-the-box "answer" given in the React documentation for how to handle constructor-like functionality in a functional component. For the most part... they're right. But it depends on how specific you feel about the need for true "constructor-like" functionality.In the example you've given, that code will indeed run once, and only once, for the entire lifecycle of the component. This is ensured by the fact that you've given it an empty dependency array.
However, effects always run after the rendering cycle. Granted, in most cases, it's probably sufficient to simply use
useEffect()
and allow it to do it's processing after the rendering cycle. But in the opening comments to this article (and in its predecessor), I stated that I want my "constructor" to run:before anything else in the life-cycle of this component
If you truly want that block of code to run before the rendering cycle,
useEffect()
will not help you.Thanks for your reply!
Do you have any real-world use case scenarios that may help us understand where a
useEffect
may be less interesting to use than the solution you provided?Of course, it's gonna be on a case-by-case basis. But where I originally ran into this was when I was trying to launch API calls - calls that I knew may be a bit "laggy", so I wanted them to be launched before the render cycle.
Another use-case that I've run into is where you need to do pre-processing of variables that go beyond simply setting the initial value of a state variable. In old-skool React (meaning: class-based React components), the most common use of the constructor was to set the initial value of state variables. And in React's documentation, they talk confidently about how you don't need a constructor anymore because you can use
const [someVar, setSomeVar] = useState('theInitialValueOfSomeVar');
.But what if you're trying to initialize variables that aren't state variables? What if you want to ensure that some "tracking" variables are set to the proper state before the component even starts doing it's work?
Again... I'll stress that this is actually an "edge case". For the vast majority of components you create, you'll never need a "true" constructor-like functionality. But it's a bit silly to assume that there's never any more need for a constructor just because you can now use
useState('initial value')
.@amin had the same question I had, but since your answer was as thorough as it was, I didn't need to ask it (for which I owe you thanks!)
Perhaps
useState
itself fits better. It inherits the functionality of data initialization inside constructors of the component classes, is executed once, and does not depend on the parameters or decisions of React to flush the cache.The base use would be:
If need to get initialized data, return it as object
Ahhh, that's another interesting approach. I hadn't even considered using a function call in the constructor of
useState()
. Thanks!I am used this method everytime but i can't explain in detail like you. That's 🔥