2 ms·
> The web being the vast platform that it is and JavaScript eating so many domains may very well be a situation where not having full mastery of a language will
by kerkeslager 6y ago
> The web being the vast platform that it is and JavaScript eating so many domains may very well be a situation where not having full mastery of a language will still get you very far perhaps
Sure, it gets you into a project far enough to commit a lot of resources to it, and then falls over.
> So, perhaps the issue Heyer is lack of education, not that the ecosystem itself is unquestionably broken?
The problem may be a lack of education, but that becomes a problem with the ecosystem when the uneducated are determining the direction of the language. Classes are a feature of JavaScript now: that's irreversible damage done to the language. We can educate people about prototypical inheritance and explain to them why hacking classes over top of them is a bad idea, but that's too little too late: we can't remove classes from the language because there's already too much code that depends on them. And we can't even add classes into linters and say "don't use them", because as another commenter pointed out, core language features now depend on it. At this point, we have to embrace the suck.
And there's a fundamental opposition in the JS community to education. Education starts with admitting you don't know something, and you can see in this thread a lot of people jumping in to defend the JS ecosystem. But the type of defenses you see are telling: instead of discussing the technical merits of the decisions made, people are shooting the messenger[1] or taking it personally[2]. The only defense of JS that's vaguely technical is brundolf's post, which isn't so much a defense of JS as a strategy for writing good code in a crappy language. Not a single post here is actually defending the JS ecosystem on technical grounds.
> Also, curious, how do you handle state full actions without some kind of global? Genuinely curious
Let's say you are writing a configuration interface for virtual servers. You have three attributes on sliders bars: memory units, CPUs, and solid state drives. There's also a total cost field. The Redux global state way looks something like:
1. Have the CPUs, Memory Units, SSDs, and Total Cost in a global store.
2. Create 3 actions and corresponding reducers that update the Total Cost when any of the slider bars change.
3. Create 3 slider bar components, which dispatch to the global store with the proper actions.
4. Create a base component that renders the whole thing.
This approach already runs into some problems:
1. First, you've got three slider components that are basically the same. You can pull out the rendering code into a Slider superclass, and that reduces the repetition, but your reusability is limited: every time you want to add a slider elsewhere in the app you have to write a whole new component. And when you look at those components, you've got a lot of implicit code being pulled in by the superclass, which means you have to switch between the component and the supercomponent to understand what's going on.
2. Your reducers operate on changes in different pieces of data, but ultimately each all have to know about all three data sources to calculate the total. You can check out "Reusing Reducer Logic"[3] and that's certainly a better idea than repeating yourself three times, but you can see that there's a complexity explosion happening here.
3. In actuality, what happens too often is the duplication is just left in the three slider classes, and you have three reducers since reuse is too complex. Even if you're very disciplined about it, the complexity of achieving reuse sometimes mean you don't have time or energy to do it. In short: this design punishes reusability and rewards duplication.
4. If you want to have multiple private servers, you now have to change literally every single one of these components, because every single one needs to know in some way which server it's operating on.
Let's design the same functionality, without Redux:
1. Have a slider component which accepts a min, max, initialValue and onValueChanged callback as props.
2. Have a parent component that holds the three values in its state, and renders the three sliders with onValueChanged callbacks that update the parent component's state. The total can be rendered without being stored in state, but if you want to have multiple servers on the page, you might want to put the total in state.
At first glance, each of these components is slightly larger, and it seems annoying to have to pass all the info into the slider components. But consider the benefits:
1. All your server state is in one place. No one else stores information on servers, and nowhere else even knows that servers exist. It's decoupled.
2. Because of this decoupling, the slider is a reusable component. You can use the same slider to modify literally anything with a min/max/value, favoring composition over inheritance[4].
3. The only thing you have to understand here is React components. The complexity is greatly reduced.
4. If you want to have multiple servers on the page, the parent component is already most of the way to being a reusable component. All you have to do is expose props for the initial server configuration, and an onServerChanged handler. There's one gotcha here: changes to the sliders need to propagate up to the parent of the parent component and still trigger the proper events and re-rendering. Pitfalls can be avoided here by using an immutability library--I prefer Immer, but I imagine ImmutableJS can do the same things. You could even roll your own, since you really don't need much to handle this--a simple set of copy-on-write functions would be adequate, although they would stop being performant if your data structure gets more complex.
[1] "This sounds like the sort of thing I hear from folks who have only passively dealt with the Javascript ecosystem"
[2] "Almost every comment thread, regardless of topic, has someone shitting on front-end developers and it's extremely demoralizing."
[3] https://redux.js.org/recipes/structuring-reducers/reusing-reducer-logic https://redux.js.org/recipes/structuring-reducers/reusing-re...
[4] https://en.wikipedia.org/wiki/Composition_over_inheritance https://en.wikipedia.org/wiki/Composition_over_inheritance
- no_wizard 6y agoJust to say, I was taking a neutral position on this, genuinely not trying to get caught in the lack of details I was seeing in the back and forth, so I appreciate the detailed response. I'll say this with redux: precisely. You can achieve reusability if you build with that in mind, but even fresh projects I've seen get caught at some point on duplicating code irresponsibly here. The fact that they didn't just make this a core part of the redux experience - I'm referring to the abstraction they use in the example on the page named `createNamedWrapperReducer` specifically - to simply allow you to pass a unique identifier you can associate with a component (which it could then offload from the state container if the component is removed from the page, saving memory) or even requiring it as a prop (people might grumble, but it'd catch on regardless) would have been a much more effective way to do this. I'm shocked 5 version sin we don't have this as the default...leaving it up to the user to create a very basic abstraction around this just irks me, as I don't see how consumers benefit from that. Yet, I don't want to create _yet another library_ since redux is so well entrenched. Internally where I used to work, we had a library that basically was just what I described. You specified a special prop on a component and any reducers were created from a factory that respected the prop, worked well enough. Now, with that said, I have to say this: I think JavaScript is a great language. I think TypeScript is an even better manifestation, despite some quirks. Now, onto some technical bits. Classes are more than just fancy wrappers on prototypical inheritance. The way its going with the standards committee, and the way I've interpreted the point of having classes, is to have formal encapsulation[0]. With things like the private fields & method proposal[1] already well on their way to formal standardization, classes are starting to make more sense. They also provide a way to extend builtins in a way the engine understands (This is also the logic behind Web Components I feel like everyone misses, but that's another story). While I know there is plenty of object oriented pitfalls everyone has seen, like abuse of inheritance, I find that the very common pre-class way was just, if not more so, prone to pitfalls. Extending classes is pretty obvious to most programmers - I mean this in terms of syntax and typical expected behavior - I coded up a simple example here to illustrate just how gnarly ES5 "classes" are. https://gist.github.com/ScottAwesome/67d2d10761bd4993eec9d9d63b0f5b9e https://gist.github.com/ScottAwesome/67d2d10761bd4993eec9d9d... I also think classes provide the correct abstraction for exposing public and private APIs, as I already mentioned the private fields and method proposal, but also its more conventionally straightforward to encapsulate this even using a WeakMap to make private methods/fields unexposed to the consumer, (one of its primary use cases when it was introduced). Plain objects, without some sort of system to manage their creation (like factories) and their visibility (Object.seal, Object.freeze, Object.preventExtension, Object.DefineProperty - all of which work on classes too, of course), they're otherwise going to leak their members and its all too easy to find private APIs on object members. I know you can with classses - sans using a WeakMap or the new private fields API. I find it feasibly easier however to integrate classes when I need to encapsulate a well defined interface with properties & methods, in large part because I can keep the private APIs private more easily, via an internal WeakMap, without sacrificing extensibility. I know you can do this with objects, however its much more clear that you get a discrete instance with classes. When that sort of thing is important, I think classes are useful. I also think there is a lot of value to the mixin pattern. I also think this reaches into what most developers just expect out of a language. Arguably, the complete lack of usage of the Reflect[2] APIs I see in the wild and where I've worked tells me most people just don't reach for these kinds of APIs. Therefore from a language development point of view, I can't argue classes are bad, either. Now I understand you can get there with plain objects, and I think this is an under-utilized mechanism of the language. In fact, I think the plain javascript object are amazing, and very few languages have such a powerful construct. I'm a big fan of composition over inheritance (to which end I have leveraged the stamp specification and its toolkit[3]. I think it would have been nice to see more specialty objects (with a static `Object` factory to create them) over having to just introduce classes. Alas, they are here to stay. Truth be told, I don't use classes that often, preferring pure functions & plain objects most of the time, but sometimes there just isn't a better way to encapsulate behaviors than the a class, in fewer lines of code, that's easy to reason about and maintain. Now one trend I don't like, is that a lot of things are becoming class only APIs (Web Components being a major one). This complicates things, but you can certainly 'hide' the implementation details via factories if you don't want to expose classes directly, so far in these cases, that's pretty straightforward to do. Worth noting: I'm just not strongly opinionated enough to say you're right or wrong on this. We already acknowledged a massive education problem in the industry, one that, I will admit is in my personal experience seemingly true, that not enough people take the language serious enough to spend the time to learn how to work with it appropriately. Until that's solved, I don't think its worth fighting over other minutiae in this particular discussion. Solving the problem of good developer evangelism in the JS ecosystem is a discussion for another time, though, and one that will ultimately be futile if we can't get the major stakeholders to have some agreement on what that means. [0]: https://en.wikipedia.org/wiki/Encapsulation_(computer_programming) https://en.wikipedia.org/wiki/Encapsulation_(computer_progra... [1]: https://github.com/tc39/proposal-private-methods#private-methods-and-fields https://github.com/tc39/proposal-private-methods#private-met... [2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... [3]: https://github.com/stampit-org/stamp-specification https://github.com/stampit-org/stamp-specification