9 comments

  • discreteevent 1428 days ago
    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 1428 days ago
      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 1428 days ago
        In Javascript, closures both have identity and are mutable, which is a huge pain when using React.
        • ss3000 1427 days ago
          I would say the biggest JavaScript inflicted pain when working with React is the lack of a notion of value equality in the default mutable collections.

          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.

        • Quekid5 1422 days ago
          I realize I was quite off-putting in my original reply, but "have identity" in what way, exactly? A memory address?
        • Quekid5 1427 days ago
          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 1427 days ago
            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 1427 days ago
              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 1427 days ago
                Totally agree.

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

                • jchw 1427 days ago
                  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.
    • obedm 1428 days ago
      Thank you for reading both articles! (author here).

      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.

      • jlelonm 1427 days ago
        FWIW I thought they were both pretty spot on. In fact, you mentioned this about DNS:

        "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!

        • obedm 1427 days ago
          That's something I hadn't considered before. That's probably something that a lot of people would find valuable.

          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 :)

    • pacala 1428 days ago
      "closure is a poor mans object and an object is a poor mans closure" is true in principle. Devil's in the details. If the language forces the declaration of the object equivalent of a closure in a different scope, or even a different file, then massaging captured variables by hand, it makes objects a whole lot more laborious to use, especially for those using rapid iteration to get a feel of the design space.
    • jayd16 1428 days ago
      I can see why objects and closures are reducible to each other. As a Java dev who's written listener objects, closures seem like simple sugar to me. They hold private state or references to state. Functions can be passed references to mutable data so they are equivalent as well.

      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.

  • konaraddi 1428 days ago
    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...

    [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 1428 days ago
      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 1428 days ago
    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 1428 days ago
      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 1427 days ago
        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().

    • untog 1428 days ago
      I have to agree. I still haven’t used hooks yet and I’m sure I’ll get used to them when I do, but by comparison to this article Svelte is a breath of fresh air. It’s simple, it works. A lot of people object to it because it’s very different to React, but judging by the diagrams in the mental models here I don’t think that’s any bad thing.

      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.

    • christophilus 1427 days ago
      I like Svelte a lot, but there’s definitely cognitive load when dealing with reactivity. In React, it’s top-down dataflow, which is generally easy to reason about.

      I prefer the functional programming approach of React, but I love the performance and some of the developer ergonomics of Svelte.

      • akiselev 1427 days ago
        You don't have to use two way binding binding in Svelte. You can bind to change events just like in React and update your reactive variables directly. The primary downside is enforcing it in the code everyone else on your team writes.
  • obedm 1428 days ago
    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 1427 days ago
      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 1427 days ago
        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).

    • w4tson 1427 days ago
      I really like your style of explaining. Your diagrams are ace too. I like how they’re not too polished, they’re focused on conveying your message. You have a real talent for explaining stuff
      • obedm 1427 days ago
        That's some seriously amazing feedback, thank you for your kind words! It means a lot to have people enjoy my writing and the visuals.

        I spend a lot of time with these articles to have them as clear as possible.

    • vorpalhex 1428 days ago
      This is a wonderful guide and fits my internal monologue for explaining things well. Thanks for writing it!
      • obedm 1428 days ago
        Thank you for reading it and for your kind words! It means a lot to see this here and for people to have enjoyed it.

        I'll write many more such guides!

    • polishdude20 1428 days ago
      Hey this is really well done congrats! You should totally make one about usememo, useref, usecallback etc!
      • obedm 1428 days ago
        Thank you so much! That's in my pipeline. I'm going to work first on an in-depth explanation of useEffect since it's the most used one, and I'll cover all the hooks at some point.

        The only thing is that a lot of the same ideas are repeating themselves, but the specifics are worth explaining.

    • jatins 1427 days ago
      This is an extremely well written article. Kudos for writing it!
      • obedm 1427 days ago
        Thank you for your kind words! More to come :)
  • devit 1428 days ago
    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 1428 days ago
      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 1427 days ago
        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 1427 days ago
          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.

      • obedm 1428 days ago
        (author here) I don't have anything to add, you guys have great arguments, I just want to show appreciation for starting a good debate based on a code example I did :)
      • gbear0 1427 days ago
        I agree with the grandparent that hooks are a weird hack and it drives me nuts every time I have to create one and can't use a closure to precompute some stuff and have better control of the lifecycle on functions/data outside the actual render function. So in the end of all this I've found I much prefer the class syntax cause it just makes more sense and does a much better job (for me) at declaring code patterns and shape than trying to follow code paths in/out of different effects (which oddly enough reminds me of the horror days of following code paths through multiple levels of inheritance).

        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 :)
    • jcelerier 1428 days ago
      > 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.

      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/yaawdomh
    • malisper 1428 days ago
      Doesn't this only hand the useState hook? How would this handle something like the useEffect hook where you may want to rerun code on every render?
    • earthboundkid 1428 days ago
      You may be interested in https://crank.js.org/blog/introducing-crank which essentially works as you propose.
    • wjmao88 1427 days ago
      This syntax might look like it require less magic, but you still have to implement everything react does for hooks that takes care of re-rendering cycles.
  • _hilro 1428 days ago
    I find the images very off-putting and unclear.

    https://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...

  • iooi 1428 days ago
    > 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 1428 days ago
      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 1427 days ago
        You could mutate some shared/global state from inside a closure as a way of getting information/state out
        • obedm 1427 days ago
          (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

        • uryga 1427 days ago
          i considered adding "mutate a global" in there but didn't feel like that was relevant to @iooi's comment. maybe i should've quantified "the only thing" a bit more :)
  • wjmao88 1427 days ago
    > 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 1427 days ago
      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 1428 days ago
    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 1428 days ago
      Fix incoming . I'm using local storage to maintain the "dark mode" choice and it can't be accessed if cookies are blocked.
      • Etheryte 1428 days ago
        Thanks a lot for the quick update, really appreciate it. Works well now.
      • IshKebab 1428 days ago
        You can do dark mode purely in CSS. No need to code an explicit choice in your site.
        • mikewhy 1428 days ago
          But the point of coding the choice is ... so the user has a choice? This is a bizarre response.
          • mcintyre1994 1428 days ago
            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...
            • satvikpendem 1428 days ago
              Yes but sometimes I want a choice. For example, I use the Dark Reader extension with a pure #000 background. Many dark modes use some grey or other non-black hue, which can be quite annoying, so I usually turn them to light mode and then Dark Reader can properly convert the light theme into my preferred black theme.
            • mikewhy 1428 days ago
              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 1428 days ago
                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 1428 days ago
                  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.

                • obedm 1428 days ago
                  As the author of the site, I like switching between the two modes on all websites that support it.

                  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.

                • mountainboot 1428 days ago
                  Not me, I like both modes. Often I will have one text editor in dark mode and another in light mode.
                • dnissley 1428 days ago
                  Sometimes one implementation or the other just plain sucks -- typically it's bad dark mode implementations that obviously were just afterthoughts.
          • satvikpendem 1428 days ago
            You can make a pure CSS theme toggle as well. You can even use SVG to do it with a custom animation.

            https://codepen.io/demilad/pen/bZRjpb

          • obedm 1428 days ago
            The author here: indeed! I've debated myself over and over whether I should make dark mode the default, but I'll implement the "preferred theme" someone mentioned in another comment :)
    • obedm 1428 days ago
      author here. I wasn't aware this was an issue, sorry about that. I'll fix it
      • lsalvatore 1428 days ago
        It's cool that you fixed this, but you shouldn't apologize to this person. You published a free article on your own site. This commenter rudely criticized your site for their nontypical edge case without an ounce of respect. They are lucky to have gotten your attention at all.
        • vorpalhex 1428 days ago
          There's value in listening to your audience, and it sounds like the author didn't intend for cookies to be required to use the site.

          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.

          • obedm 1428 days ago
            This is my sentiment. I'd feel frustrated as well if I was on their shoes (visited an interesting article, but it didn't work because of something silly).

            It was also unexpected. I didn't even know localStorage could be broken by disabling cookies haha.

          • seattle_spring 1428 days ago
            > though it's obvious they're frustrated (understandably)

            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.

            • vorpalhex 1428 days ago
              When I visit a news site, to read an article, I get a video that scrolls along with me and autoplays. So I disable video autoplay.

              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.

              • obedm 1427 days ago
                I whole-heartedly agree with this. Unless you implement a must-have feature based on localStorage or whatever, your site should still fully (or at least partially) function without it.

                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!

    • danlugo92 1428 days ago
      What do you use for cookie blocking?
      • Etheryte 1428 days ago
        Nothing special, I've turned off both first party and third party cookies in the browser settings. I add sites I use regularly to a domain whitelist [1], other than that I don't use much anything for cookies anymore. Historically I also used to use Privacy Badger [2], but with my current setup it isn't really all that relevant.

        In broader tracking and privacy context, I also use NextDNS [3] and uBlock Origin [4].

        [1] chrome://settings/content/cookies?search=cookies

        [2] https://privacybadger.org/

        [3] https://nextdns.io/

        [4] https://github.com/gorhill/uBlock

        • sopromo 1428 days ago
          You could give uMatrix a try, I use it to block cookies, images, media and iframes. If you really want to allow a site to use all that it is a bit easier to do (I think)
        • pier25 1428 days ago
          So if a site requires cookies for authentication you manually add that site every time?