31 comments

[ 3.3 ms ] story [ 70.5 ms ] thread
get completed () Is "get" som es6 thing?
No, just not commonly used; getters and setters aren't terribly useful in a language that allows such easy direct modification of objects (as opposed to say Java)
But transparent getters and setters can be used to emulate private or immutable properties:

    function Foo() {
      var _bar = 42;
      return {
        get bar() {
          return _bar;
        }
      } 
    }

    var foo = Foo();
    foo.bar
    /*
    42
    */
However the fact that you can transparently replace a simple property by a computed one (e.g. for backwards compatibility or to trigger side effects in observables) does make explicit, Java-style, getters and setters less useful.
In our shop where we have built many high-performant, and pretty complex (various types of security tools and all that) react apps, the state is managed at the top-most component and it is passed down to all other components as props. It works great and it is functional all the way.
So you pass the entire state object, however large it might get to all child components?
For child components that don't need the entire object, leverage destructuring assignment and it becomes a lot nicer than it sounds!
So how do you deal with UI containers that don't really care about most of the state, but still need to pass it on to their child components?

It sounds like tracking this by hand ("what is needed where") could get messy, though I don't have experience with this approach.

> So how do you deal with UI containers that don't really care about most of the state, but still need to pass it on to their child components?

That's more or less the questions that they were asking in 1995 when they made the design patterns and conceived dependency injection.

Someone else beat me to the joke of "turtles all the way down", but that's mostly it.

I'll first refer to this Twitter thread: https://twitter.com/JEG2/status/902534133427134465

Now, the reasoning is: if you pass everything down, then things get interesting to reason about. Given a state to a component, it will always render the same way (give or take, local state and all). That is only true if you reason about children components as implementation details of the parent. That is, if a parent component renders a child one, and the child needs a prop, the parent should have that prop too.

Otherwise, you could render the parent 6 times with the same props and get 6 times a different result based on some undefined (at the parent level) magical global side effect.

Doing it this way makes your component tree essentially fractal: at any point you could detach an arbitrary component, put it in a library, and use it in another app: there's no assumption. Any part of the app is in itself a small app.

Now, the main issues is the boilerplate of typing it all out (which is minimized if you use a single "model" object at every level and either pass it as a whole, or a smaller part of it, to any child): if you use regular props for every value, you'll have top level components with 100+ props.

The other issue is maintenance. What happens when you move things around. That part really begs for a type system. Use Flow or TypeScript, and just let the compiler bark at you. When the compiler is done barking, you're done fixing, and you have a full statically checked view of your model dependencies. That's awesome!

The rest is mostly cultural. At which point does a developer throw their arm in the air and say "I don't want to do this!". A few months back, Jordan Walke did an Ask Me Anything on Discord where he said he had a pretty high tolerance for passing stuff down manually. I assume that explains some design decisions in React (eg: like how context was not a first class citizen earlier on), but I don't want to put words in his mouth.

For an example of this concept, look at Elm, where that's the primary way to build things. A fully fractal architecture where models come from the top and get passed all the way down to all the turtles. The way you build your model changes a bit (in apps Ive seen, it's more of a mirror of the component hierarchy, and less of a conceptual, normalized model), but boilerplate isn't that bad. Higher than the typical React/Redux dev will tolerate, which itself is higher than what most web devs will...but people who do it generally end up with very high quality apps that are trivial to maintain.

That works well for leaves of the component tree, but not in deeply nested hierarchies. You never now whether some subcomponent in your <NavBar> requires state.serverInfo.frobnicationCount...
Yeah, especially if you combine that idea with Flow/TypeScript or Reason. In fact that's why Redux isn't really used in Reason very much, with algorithmic data types, exhaustive pattern matching and all the power of OCaml you don't need it!

I enjoy the simplicity of it so much that I've now entirely adopted Elm -- type safety, super simple but powerful, and completely based on uni directional data flow with a compiler that is the most helpful I've ever used all wrapped up in the same State -> View function hierarchy that I adore in React.

You probably meant "algebraic data types" :)
I most definitely did :) remind me not to type while still half asleep!
Passed around local state vs global state is indeed an interesting topic. Honestly I think both are nice in good hands.

The library I made supports both of them, but I did not mention local state management for the sake of simplicity. You can find it here: https://github.com/solkimicreb/react-easy-state#easycomp (it will take 1 min to read).

Doesn't it get clumsy to pass state as props to deeply nested comps?

Yup! This is my approach when developing side projects. Keep everything explicit.
Surprised to see the state store caring about events. Checking for a keycode in your system to add an event to a store feels like a violation of separate concerns. That was an immediate turnoff for me with this post.
Valid point. You can move that logic to the component and leave the store for pure data manipulation and handling only. I do that for bigger projects, but in this case I wanted to make things short.

The goal of this series is not to teach patterns but to promote a simple and flexible stack that you can use with the patterns that you like. (In your case you can move the view specific logic to the level of the components).

I came here to say the same as whalesalad. This is not just a "you can do that if you wish" situation. This is more a case of "so much anti-pattern that it turns off readers".
As you wish (: You convinced me, I edited the gists and removed the antipattern. Please review it if you have a spare minute. Thx!
Most welcome. Looks so much better. And it was whalesalad to report it first :) Have a great day!
I have wondered if it would be good for debugging (assuming you're using Redux or something with a time traveling debugger) to put UI events in the store, too. Then you can have a complete picture of "how did we get to this broken state"if you need it. I would keep that stuff separate from data with a prefix, so that you could easily distinguish between the two.
Nice idea!

Did you check out mobx?

My guess is that it would be almost as easy and a bit more mature.

Sure, I know about MobX. I use ES6 Proxies instead of the getter/setter based transparent reactivity they use. It allows me to have less edge cases in exchange for narrower platform support.

MobX is also moving in the Proxy direction with v4, but I do not agree with some of their architectural decisions (mostly the synchronous execution), so I made a separate library. We can discuss these later if you are interested.

Neat. Did you get any inspiration from Vuex in creating the store? The global store and building it out of a POJO are pretty similar patterns that I happen to like.
No, I have never used Vuex, but I think Vue is an awesome framework. My main inspiration was monitoring my problematic situations and state management needs during app development in various frameworks.
I'm a big fan of react-easy-state. Unfortunately its foundation of ES6 proxies precludes its use in Internet Explorer.