
A Visual Guide to React Mental Models: UseState, UseEffect and Lifecycles - mariuz
https://obedparla.com/code/a-visual-guide-to-react-mental-models-part-2-use-state-use-effect-and-lifecycles/
======
discreteevent
In part 1 they talk about everything being a function including react
components and closures. But I come from the "closure is a poor mans object
and a function is a poor mans closure" perspective. In other words everything
is an object. This is most likely because I started programming in the early
90s when first-class functions were not that common in popular languages (C
had function pointers but it wasn't pretty)

But either way it works fine. Everything is a first class 'thing' that has
properties that can be parameterised. If the 'thing' is an object the
properties are alive and can be mutated (if mutable) for the lifetime of the
object. If the 'thing' is a function the properties (arguments and local vars)
are alive for the duration of the call.

When first class functions became mainstream though, I couldn't really
understand the idea that they were fundamentally new compared to objects (in
fairness this was only put forth by a few people)

~~~
Quekid5
I lean towards closures having primacy. FTR, I learned OOP way before I'd ever
heard of functional programming.

My reasoning is based on two things: Firstly, objects have identity and this
is a superfluous notion for most cases. Functions just "do things" to their
inputs and return the result. Secondly, OOP is fundamentally predicated on the
notion of mutation[0], but I think that is totally antithetical to local
reasoning (chunking). The latter may just be a preference, but IME it seems to
bear out. Mutation seems to be a consequence of the focus on identity, now
that I think about it after writing that footnote.

> I couldn't really understand the idea that they were fundamentally new
> compared to objects (in fairness this was only put forth by a few people)

Just a historical note: First-class functions were invented waaaaay before OOP
was a gleam in somebody's eye.

[0] The only non-mutating OOP system I know of is O'Camls version of OOP, but
it's not very popular because it's just... unnecessary. It also does away with
"identity", now that I think about it.

~~~
7786655
In Javascript, closures both have identity and are mutable, which is a huge
pain when using React.

~~~
Quekid5
JS... the "winner" of the Accidental Complexity game. (It _was_ made in a
couple of weeks, so it's excusable, but... it just hurts so much that _this_
was the language that conquered the web :| )

~~~
eitland
Yes, and realizing there is probably a larhe subset of people who'd say they
love JS but hate PHP can make a man who know both too well confused. Or at
least aware of the hypocrisy ;-)

~~~
jchw
To be fair, PHP5 really was quite a nightmare for honestly a lot of reasons
that JS was never. I think most people remember PHP5 and compare it to fairly
modern JS, but I must say, modern PHP is a lot better than older PHP (i still
prefer just about anything else.)

~~~
eitland
Totally agree.

But comparing modern Javascript to ancient PHP would be even more
unreasonable.

~~~
jchw
Yes, to be clear, I agree. I think most people just don’t realize PHP actually
did improve. I’ll admit until digging into a modern PHP library I didn’t even
believe it could be meaningfully better.

------
konaraddi
I think a correction needs to be made in Part 2 "How Does State Change"[0].
The author says "Long story short, don’t expect state to be updated right
away" in the context of using `searchValue` right after calling
`setSearchValue`. This gives the reader the impression that state is sometimes
updated right away and sometimes not so we can't depend on it. However, it's
impossible for a useState variable (e.g. `searchValue`) to be updated without
a re-render [1]. Thus, using `searchValue` immediately after `setSearchValue`
will always be outdated. So state is only updated after render.

[0] [https://obedparla.com/code/a-visual-guide-to-react-mental-
mo...](https://obedparla.com/code/a-visual-guide-to-react-mental-models-
part-2-use-state-use-effect-and-lifecycles/#how-does-state-change)

[1] The `setSearchValue` updates a value that's outside (and captured) by the
closure, React will re-render the component since setSearchValue was called
(idk how this works behind the scenes and i think rendering is async), during
the re-render `searchValue` and `setSearchValue` are assigned values again,
and thus `searchValue` will finally be the updated value.

~~~
obedm
You're right, the code is not explained clearly enough. I intended it to be a
"don't do this!" part, but I didn't really explain that.

Great attention to detail, thank you for pointing it out, I'll update it!

------
pier25
I haven't used React since before hooks were introduced but I switched to
Svelte a couple of months ago and now I find the cognitive load of using React
totally unnecessary. Even more now with hooks.

In Svelte you never think about these or other considerations (eg: useMemo).
Just update some state and that's it. Svelte will render consistently faster
than React, with less code to write (and maintain).

It's true with React you get a bit more flexibility but I have yet to find a
situation that cannot be solved with Svelte.

~~~
superhawk610
To say that you never have to think about those considerations in Svelte is
pretty unfair - reactive declarations/statements are functionally equivalent
to hooks, and they also carry a somewhat nonintuitive cognitive load until
you've begun to "think in reactivity". It's also worth considering that hooks,
while unintuitive in function, do use intuitive, non-magical syntax (unlike
Svelte's reactive syntax). FWIW I like both, I just think they're both equally
unintuitive until you make the mental connection.

~~~
pier25
I guess we're all different, but I've never had to think much about reactivity
in Svelte.

Most of it can be explained in a HN comment:

Component state just does its thing. You change it with = and the component is
re-rendered.

Writable stores need to trigger changes with set() and you need to use $
whenever you reference it in a .svelte file so that Svelte handles
subscription automatically.

Reactive statements $: are a bit more complex. Every variable inside those is
referenced by the compiler so that it knows when you are mutating it. Either
with = or if it's a writable store with set().

------
obedm
I'm the author. It's unbelievably amazing to be on spot #1 in here!

Thank you to whoever uploaded it!

I'm happy to answer any questions

~~~
bbx
I learned a lot about useEffect thanks. I initially clicked for the visual
explanation but ended up enjoying the written explanation more.

I think the problem with your visuals is the lack of depth in the colors.
Especially because of the gradients, all the parts kind of blend together, so
there’s no clear distinction between a parent component and its children for
example. I think having clear distinct colored boxes with borders would help.

~~~
obedm
Thanks a lot for the feedback! This is something I've struggled quite a bit to
improve, and I think the visuals for part 2 are better than part 1.

But you're right, I'm not satisfied yet with how the boxes show, I'll try to
improve them for part 3 (and other articles that'll use them).

------
devit
While equivalent to a proper approach, it feels like hooks are an unnecessary
hack.

What could be done is to have the component function return a closure that
returns the VDOM nodes instead of the VDOM nodes themselves, and then the
hooks would be run only once, since rerendering would only run the returned
closure and not the whole component function, and there would be no need for
the magic or rules.

Like this:

    
    
      const Component = component((comp) => {
        const [show, setShow] = useState(comp, false);
    
        return (...props) => (
          <div>
            <button onClick={() => setShow(!show())}>Show Menu</button>
            // Mounted with show = true and unomunted with show = false
            {show() && <MenuDropdown />}
          </div>
        );
      });
    

Where the changes are that the hook takes the component as an argument, show
is now a function rather than a value and a closure (that takes the props) is
returned rather than returning the VDOM nodes directly.

The React Hooks API is essentially equivalent to this, but inelegant,
unintuitive and somewhat inefficient.

~~~
spankalee
What's the benefit of this over class syntax? If we had decorators in the
language already we could make field reactive:

    
    
        class Component extends React.Component {
          @state show;
    
          render() {
            return
              <div>
                <button onClick={() => this.show = !this.show}>Show Menu</button>
                // Mounted with show = true and unomunted with show = false
                {this.show && <MenuDropdown />}
              </div>
          }
        }
    

Hiding the underlying stateful objects that back React components seems like
it causes more confusion and complicated APIs than it's worth. Likewise
closures returning closures and state defined with function calls seems like
re-inventing classes.

Just make the component itself stateful and the lifecycle explicit and it's
all so much simpler. Then attack the problem of composing JS classes from
pieces with tools available to all JS classes, not just the framework at hand.

~~~
nikki93
It seems like the main benefit the authors of hooks get at is being able to
encapsulate a library's logic into hooks that you can reuse, so eg. you could
do `const [data, loading] = useQuery('some query');` and the query is aware of
lifetime etc. without needing to use mixins or multiple inheritance or traits
or something else like the component or ECS paradigms from game engines (that
each have their pros and cons).

You could have a stateful `this.query` object in your class, but you basically
have to spread the code for that around (calling mount / unmount handlers or
events) vs. just mentioning it in one place and having it be able to use more
hooks internally etc.

It's basically like an `import` statement for your component.

~~~
spankalee
This is my point about attacking composition with what we have for classes -
because reuse and composition are problems not limited to UI widgets.

Your query example is easily handled by a mixin:

    
    
       // Queryable provides this.data and this.loading and
       // hooks into the lifecycle
       class MyComponent extends Queryable(Component) {
         query = 'some query';
    
         render() {
           // ...
         }
       }
    

You can also use helper objects:

    
    
       class MyComponent extends Component {
         query = new Query(this, 'some query');
    
         render() {
           // ...
         }
    
         connectedCallback() {
           this.query.connect();
         }
       }
    

I understand that you have to wire up the lifecycle methods this way, but it's
at least low-magic and easily understandable. I don't mind it.

And even though decorators aren't standard yet, they can be used for
composition too.

My point is that these problems aren't unique to React and deserve patters and
solutions for all JavaScript programming. React is statefull and object-based
under the hood, but just trying to hide it.

------
_hilro
I find the images very off-putting and unclear.

[https://obedparla.com/static/1c6caf6685f0bd3381d84a8166579de...](https://obedparla.com/static/1c6caf6685f0bd3381d84a8166579de1/83d9f/react-
rendering-mental-model-from-part1-cleaner.webp)

What's with the wavy lines as representation?

Similarly for an image titled 'Javascript closures visualized', we get
[https://obedparla.com/static/e0caedb67992235db77271808958da3...](https://obedparla.com/static/e0caedb67992235db77271808958da3d/e922d/javascript-
closure-mental-model.webp)

------
iooi
> In short, a closure is like a semi-permeable box, letting information from
> the outside get in but never leaking anything out.

This is just wrong. For example:

const counter = () => { let count = 1; return () => count++ }

~~~
uryga
the counter's value isn't leaking out, you're _explicitly returning it_.
that's the point – the only thing you can "get out of" a closure is what it
returns when you call it

(see also: the JS pattern of using closures to create objects with truly
private "fields")

~~~
NegativeLatency
You could mutate some shared/global state from inside a closure as a way of
getting information/state out

~~~
obedm
(author here) this is true! That's what I'm trying to explain in the `state`
section of the article.

State lives in a "global" and the component is modifying it. So it's "leaking
info out".

It's hard to make comprehensive mental models that cover all the bases and
leaves nothing out

------
wjmao88
> React state is scoped to the outer-most box, that way it doesn't change on
> every render

The state (of the hooks) for individual components are still scoped to that
component only, its just stored in the "outer-most box", and the structure
corresponds to the component tree. If a component unmounts and remounts, the
prior state is lost.

~~~
obedm
You're correct, this is something I tried to convey in my article but I didn't
want to spend too many words on it.

When retrospecting about it, I realized I didn't think of it this way, even
though it's correct. My mental model is like "state is just kept, don't worry
about it".

Thank you for pointing it out though, it's hard to be accurate and succinct

------
Etheryte
Another day, another site that's just text and images but for some reason
won't work with cookies disabled. I know not allowing arbitrary cookies on my
machine en masse is my own choice, but I cannot comprehend why a page like
this wouldn't work without them.

~~~
obedm
Fix incoming . I'm using local storage to maintain the "dark mode" choice and
it can't be accessed if cookies are blocked.

~~~
IshKebab
You can do dark mode purely in CSS. No need to code an explicit choice in your
site.

~~~
mikewhy
But the point of coding the choice is ... so the user has a choice? This is a
bizarre response.

~~~
mcintyre1994
I think they mean using CSS `prefers-color-scheme` the user can set a
light/dark preference at the browser level and your site doesn't have to have
its own implementation. [https://developer.mozilla.org/en-
US/docs/Web/CSS/@media/pref...](https://developer.mozilla.org/en-
US/docs/Web/CSS/@media/prefers-color-scheme)

~~~
mikewhy
I'm aware of the prefers-color-scheme stuff added to browsers. AFAICT, only
Firefox supports setting it at the browser level, while others will inherit
from the system.

This would still remove the visitors ability to say "I want THIS SITE to be in
light/dark mode", without affecting all other sites / apps on their system.
This page could support reading from the browser/OS, while still giving the
choice on top of that.

Again, this choice was probably implemented ... to give the user a choice.

~~~
adrianmsmith
May I ask (genuine question), why would anyone need a choice per site? Surely
some people are “light mode” people and want everything in that mode and
others are “dark mode” people?

~~~
mikewhy
Some people are "light mode" people, and some are "dark mode".

Some people may not want everything to be light or dark mode. I am one of
them.

Some articles may not work well with dark mode.

