DEV Community

Discussion on: Stahhp With The Outdated React Techniques!

Collapse
webketje profile image
webketje

I would argue that the code styles compared in this article are both equally valid, and that the rest is pure preference. Whether you use .bind in the constructor or define class fields to which you assign arrow functions and then use that as methods depends on:

  • familiarity preference,
  • what stage of ES proposal syntax you want to use and are comfortable preprocessing before it is offically in the spec: in this case github.com/tc39/proposal-class-fields is a Stage 3 proposal (fairly mature).
  • how flexible you'd like your component setup to be: in your example, you cannot, say, map initial props to initial state, as you no longer have access to the constructor(props) param.
  • what you'd like to actually do behind the scenes: if you use arrow syntax for all methods, you lose all re-usability of prototype methods, and every time the component is recreated, all "methods" are redefined on the instance at component instance initialization.

React created the 'binding' problem by first advocating for class components using this everywhere, and then requiring developers to pass functions without their context as props down to child components to achieve certain things. Then all React devs started to advocate for arrow syntax class methods for consistency and because block scope is easier to understand than function scope (arguably, both are true).

However, render() should never be arrow-syntaxed in normal React usage as it is never passed as a prop to other components. In mithriljs, -another JS component framework that uses hyperscript primarily-, the view method MUST be defined without arrow syntax, because it enforces prototype method re-use (see the ES6 class example). But the superior approach is to define the initial state in a closure and reference it by name instead of using this (e.g. see mithril's closure state example). It seems that with hooks, React is finally going where mithril already was 5 years ago, with React again hiding parts of what it does "magically" behind hooks.

Collapse
bytebodger profile image
Adam Nathaniel Davis Author • Edited on

I think we're mostly in agreement and I appreciate the feedback.

I would argue that the code styles compared in this article are both equally valid, and that the rest is pure preference.

My argument is not that either/any approach is "invalid". My argument is: Don't purposely (lazily) show the most old-skool, verbose example that you know to use for class-based components, and then hold up that particular syntax as an example of why functional components are better. Maybe functional components are better. But if they are, it's not because they're more terse than class-based React components that could've been written in 2015.

It'd be like me trying to tell you that electric cars are superior to gas cars. And to "prove" my point, I show you a comparison of a Tesla vs. a Model T. Maybe electric cars are better. But comparing a Tesla to a Model T does nothing to prove/disprove the point. It's apples-n-oranges.

how flexible you'd like your component setup to be: in your example, you cannot, say, map initial props to initial state, as you no longer have access to the constructor(props) param.

Yes, this is true. But again, if I'm trying to make a "case" that functional components are better, it's kinda lame to throw in all those unnecessary LoC onto my class-based example when those extra LoC don't serve any purpose in the component itself. If someone were using an example that leveraged initializing state with props, then I would totally understand them using the constructor example. But instead, they use this dead-simple example where the state variables are not initialized with props, and then they point to all those extra LoC in the constructor of the class-based component - as though that is a pertinent example in the functional component's favor.

what stage of ES proposal syntax you want to use and are comfortable preprocessing before it is offically in the spec

I've never seen a production deployment of a React app that didn't also leverage Babel. Which makes the whole point of "what's officially in the spec" kinda moot. Granted, every dev shop is perfectly within their rights to decide, for their own purposes, what they're comfortable seeing in their own codebase. But as long as the team's comfortable with Syntax A, and Syntax A can be transpiled through Babel back down to ES2015 (or earlier, if desired), it shouldn't much matter.

if you use arrow syntax for all methods, you lose all re-usability of prototype methods, and every time the component is recreated, all "methods" are redefined on the instance at component instance initialization

I personally find this to be a strong argument in favor of using arrow syntax for all methods. Any time I've personally seen the class methods get reassigned, it's always been a mistake/bug. That being said, I'm sure there are some cases where you could argue for such "flexibility". But it feels like an extreme edge case to me.

React created the 'binding' problem by first advocating for class components using this everywhere, and then requiring developers to pass functions without their context as props down to child components to achieve certain things.

I don't disagree. But given that this has, essentially, been "solved" in React now for quite some time, I just think it's lame to keep trotting these examples out there to illustrate why functional components are The Epic Hotness and class-based components are The Ultimate Fail. Again, I'm not saying that functional components are worse. But if someone's gonna do a tutorial to show why they're better, at least have the intellectual honesty to do an apples-to-apples comparison.

However, render() should never be arrow-syntaxed in normal React usage as it is never passed as a prop to other components.

This is the only part of your reply that I honestly don't understand and/or agree with. But it's not really that big of a deal. I couldn't care less if the render() method is ever "passed as a prop to other components". I'm not seeing even a single piece of lost/broken/changed functionality that results from defining the method as render = () => {...}.

Collapse
webketje profile image
webketje

Hey Adam, thanks for the reply, I take the point of the article (that you further clarified here).

My point of the "render should never be arrow-syntaxed" is: 1) it is highly unusual for React components' render methods to be invoked by anything but React's internal rendering cycle, so the danger of this context mismatch is 0 (except if you do call it directly).
2) JS prototype-attached methods allow devs to define a single method that is the same for, and can be used by any amount of instances, without being redefined (or bound). 3) Defining a class field as method = () => {} will transpile to code inited in the constructor, (this.method = function() {}), and not Comp.prototype.method = function () {}, leading this.method to be re-defined for every component instance.

So say you have 1000 instances of 1 component, that's 1000 function re-definitions where you could've had 1. You won't notice this perf enhancement unless you have a massive amount of component instances that define a massive amount of methods though.

Thread Thread
bytebodger profile image
Adam Nathaniel Davis Author

Ahhh, OK. That does make sense. I hadn't thought of it in that context. But I suppose that, since every component has a render() method (in a class-based component), that could certainly be inefficient (although, as you've noted, it would only present itself in a very sizable app).

Thank you for taking the time to explain that to me!