A Visual Guide to React Mental Models: UseState, UseEffect and Lifecycles
obedparla.com
obedparla.com
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)
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.
Coming from a ClojureScript background, it pains me to have to deal with all the idioms in React that hack around this fundamental impedance mismatch by forcing users to carefully maintain reference equality for collections between renders for perf optimizations, and more recently for correctness in hooks like useEffect.
But comparing modern Javascript to ancient PHP would be even more unreasonable.
When I write these sometimes-overly-simplified-articles I'm terrified of explaining something wrong, or that my knowledge may be incorrect on a very specific point.
I really like the way you express "everything is a first class 'thing'", I never thought of it that way.
"As I read articles and tried stuff out (and broke my server config more than once) I started to grasp the system, to get glimpses into how it all worked, until eventually it “clicked” and I felt comfortable working with it."
Have you written about your mental models for DNS? Color me interested!
Out of all the questions my students ask me, there's always confusion about the way servers talk to each other, or what they even are.
I'll definitely write about this now haha. Thank you for the idea :)
However, there is the concept of a pure function (one that is unaffected by and causes no side effects) which I think can be argued is fundamentally different.
[0] https://obedparla.com/code/a-visual-guide-to-react-mental-mo...
[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.
Great attention to detail, thank you for pointing it out, I'll update it!
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.
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().
An aside, and I know complaints about hooks are very boring now, but I remember when React first arrived on the scene. The API was a delight. shouldComponentUpdate, willReceiveProps, etc. Everything was so clear from the first second you picked it up. Now? I’m less sure.
I prefer the functional programming approach of React, but I love the performance and some of the developer ergonomics of Svelte.
Thank you to whoever uploaded it!
I'm happy to answer any questions
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.
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).
I spend a lot of time with these articles to have them as clear as possible.
I'll write many more such guides!
The only thing is that a lot of the same ideas are repeating themselves, but the specifics are worth explaining.
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.
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.
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.
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.
However, there is a major value hooks provide over class components and that's the composability of the lifecycle effects. The problem with class components is that code for a lifecycle is broken across multiple functions and interweaved with other effects. This happens because the declarative model of the component tree clashes with the imperative ordering we code up for lifecycle functions attached 'within' each class component. The fix is to turn lifecycle effects into first level declarative concepts themselves (like in hooks) so they're not 'within' the component, instead they're more of a has-a relationship.
This would actually be rather simple to do, drop the lifecycle methods in a component, and add a new Component.addEffect function (or, cause I'm a big fan of decorators, you could have some decorator to declare what effects should be used). And each effect would be another class that inherits from Effect. I think the hardest part to think about is how you interweave the effects like people do in hooks as one output becomes the input for another, but I think this is probably the wrong way to think about it. Instead if you think of it as an Effect tree then each Effect is just a 'render' function where you're passing in other effects as the props and the output is passed down the tree, so you'd just need an inline effect function that would compose 2 effects together the same way we do with components. At this point the conceptual model is extremely easy cause it's declarative trees all the way down!
RootComponent
|- ComponentA
|- effects
||- lambda effect -- myClickEffect
|| |- myStateEffect
||- lambda effect -- myKeyEffect
|| |- myStateEffect
||- myCleanupEffect
|- children
|- ComponentB
|- effects
|- children
|- ComponentC
|- effects
|- children
...
Anyone wanna pass this idea onto the react team that'd be awesome cause I'd love to see this added for Class components to make them more composable as well as having better sharing of ideas between hooks and class components :)what could be done is actually using a proper reactive language where things look like this, instead of hacking HTML, JS and whatnot in an unholy mess of syntax:
Button {
text: "Show menu"
onClicked: menu.visible = !menu.visible
}
Menu {
id: menu
}
e.g. a bit like this: https://tinyurl.com/yaawdomhhttps://obedparla.com/static/1c6caf6685f0bd3381d84a8166579de...
What's with the wavy lines as representation?
Similarly for an image titled 'Javascript closures visualized', we get https://obedparla.com/static/e0caedb67992235db77271808958da3...
This is just wrong. For example:
const counter = () => { let count = 1; return () => count++ }
(see also: the JS pattern of using closures to create objects with truly private "fields")
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
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.
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
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.
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.
Like someone above said, some sites implement it as an after-thought. In my case, I'm giving more love to dark-more because there are simply more nice color combinations in dark more than with white.
I don't think the commenter was being rude either, though it's obvious they're frustrated (understandably).
You can choose to cooperate with others or fight them. Usually when we cooperate, things are improved.
It was also unexpected. I didn't even know localStorage could be broken by disabling cookies haha.
It reminds me of the meme where a guy is riding his bike, inserts a stick into his own wheel while riding, falls over (understandably), and then blames someone else for his fall.
This breaks some video conferencing apps. It's a choice I have to make - some broken apps or a bunch of annoying websites.
Some websites use my cookies and localstorage to track me across properties. They do this so they can harvest my profile data and habits and sell them for profit without my permission.
Again, it's a choice and I think it's a fair one to make.
As engineers, we don't exist to punish our users for making choices different from ours. That's a great way to end up without any users. We should make their experience as good as we can and give them value. Sometimes that isn't possible - I can't make a video conference app that's immune to blocking autoplay. That doesn't mean we shouldn't try.
In my case, I was aghast my blog didn't work without cookies. I don't even have ads running on it, and it's static, it should work on my grandmother's toaster!
In broader tracking and privacy context, I also use NextDNS [3] and uBlock Origin [4].
[1] chrome://settings/content/cookies?search=cookies