React Is Eating Itself
Tags: react, javascript, redux, hooks
Published: 2020-03-09 23:51:32

A few posts ago, a thoughtful commenter said they'd like to understand "why React is so compelling for you".  I tried to outline _some_ of those reasons in that post (because Redux goes against so much of what I find beautiful in React).  But I didn't really explain how core React can be so _elegant_.  Nor did I properly highlight how so many current-day practices are slowly eroding that elegance.

(That prior post was titled _The Splintering Effects of Redux_ and can be found here: https://dev.to/bytebodger/the-splintering-effects-of-redux-3b4j)
<br/>

## "Locus of Control" vs. "Separation of Concerns"

When nearly _all_ of our enterprise applications were delivered by server-side processing, MVC ruled the day.  MVC was a useful pattern because it kept us from blindly shoving **_ALL THE THINGS!!!_** into a single class/page/module/function.  It made us more vigilant about separating data (Model) from display (View) from logic (Controller).  

If there's any "problem" with this pattern, it's that it started to get... "fuzzy" as our apps got pushed mostly, or entirely, into the UI layer.  There are still devs who try to adhere to the idea that **all** data calls should be separated from **all** display which should be separated from **all** logic.  But that paradigm doesn't provide as much value in a Single Page Application. 

The current generation of "rich" internet applications makes these distinctions challenging (if not outright erroneous).  Does that sound like heresy to you?  If so, consider that the more real-time processing capability which gets pushed-to/built-in the browser, the more the browser effectively becomes a true _console_.

Have you ever built a true _console app_??  (It's OK if you haven't.  But it's useful to this topic if you have.)  Although it may feel archaic today, if you ever built, say, a small Visual Basic app designed to run directly in the operating system, you might start to feel what I'm getting at.

In a console app, you typically have a variety of _components_ that you can position somewhere on the screen.  Most of those components come with a set of common features:

1. Attributes that control the component.  Usually, these attributes define the component's _initial_ appearance/behavior.<br/>

1. An internal store that holds ongoing information about the component.  This could include: the component's position, current display features, information about related components, etc.<br/>

1. Pre-existing or programmer-defined actions.  These events are frequently triggered by a user's interaction with that component.<br/>

1. An interface for this component to "talk" to other components, or to interact with other data stores.<br/>

1. Some components are standalone. But many are _container_ components, capable of housing one-or-more child components.

Notice that there's nothing in this component model that even attempts to satisfy an MVC pattern.  Under a rigorous MVC approach, the component's own internal memory would be handled _somewhere else_ - in the Model.  Any of the logic that is triggered through its actions would be handled _somewhere else_ - in the Controller.  Even any tweaks to the component's display features would be handled _somewhere else_ - in the View.

So is a console-application component somehow "bad" programming?  After all, here we have one "thing" - a _component_ - that has logic, and data, and display all wrapped up in one bundle.  So that's gotta be a problem... _right_??

Umm... no.

You see, the console component that we're talking about here can reasonably handle logic _and_ data _and_ display, all wrapped up into the same "thing", because we only give that component power over those things that should naturally be in its _locus of control_.  

In other words, the console component can (and _should_) handle the data (the Model) _that belongs in that component_.  It can (and _should_) handle the display (the View) _of that component_.  It can (and _should_) handle the logic (the Controller) to process the actions that are triggered _from that component_.
<br/>

## A Different Kind of Console

With every new browser update, they come ever closer to being true _consoles_. And if you're a React developer, a lot of this verbiage probably sounds very familiar to you.   

React has components.  Those components (can) have their own internal state.  Every component has a `render()` function to handle its own display (which can return `null` if there _is no_ display to be rendered).  And they can have any number of associated functions, which handle the logic associated with their own actions.

This can all be demonstrated with the most basic of examples:

```javascript
import React from 'react';

export default class Counter extends React.Component {
   state = {counter:0};

   decrement = () => {
      this.saveCounter(this.state.counter - 1);
      this.setState(prevState => {counter:prevState.counter - 1});
   };

   increment = () => {
      this.saveCounter(this.state.counter + 1);
      this.setState(prevState => {counter:prevState.counter + 1});
   };

   render = () => {
      return (
         <>
            <div>Counter = {this.state.counter}</div>
            <button onClick={this.increment}>Increment</button><br/>
            <button onClick={this.decrement}>Decrement</button><br/>
            <button onClick={this.reset}>Reset</button><br/>
         </>
      );
   };

   reset = () => {
      this.saveCounter(0);
      this.setState({counter:0});
   );

   saveCounter = (counter = 0) => {
      fetch(`https://127.0.0.1/saveCounter?counter=${counter}`);
   };
}
```

In this scenario, I'm thinking of the entire `<Counter>` component as, essentially, a "thing".  A "logical unit", if you will.  So even though there's a lot going on with this little example, it's all part of one _logical unit_.  

The `<Counter>` component has its own memory (state).  But that actually makes sense, because the only memory it's responsible for is related directly _to this logical unit_.  

It has its own layout (rendering).  But that makes perfect sense, because it's only rendering the items that are directly related _to itself_.

It has actions - and the logic needed to process those actions.  But again, that makes perfect sense, because those actions are all directly related _to itself_.

And finally, we even have the initial phases of a data layer, as witnessed in the `fetch()` inside `saveCounter()`.  But that makes a lot of sense here, because the data it's saving is specifically related _to itself_.  

In other words, even though this one component does rendering, internal data, external data, and logic tied to actions, that all makes _sense_.  Because all of that stuff falls under this component's _locus of control_.

I'm not gonna lie.  I see a certain _beauty_ in this.  If I want to know what's going on with a particular component, I look _right inside the component's code_.  I know... radical concept, huh?  And it's not like I'm just making this stuff up on my own.  When you look all over React's core docs, they give many examples that are very similar to this.  

But code like this is becoming increasingly rare "in the wild".  The beauty of this model is disintegrating - because React is eating itself.
<br/>

## This Is Why We Can't Have Nice Things

Outside of blogs and tutorial sites, you rarely see much code like above in "real" applications.  And I don't mean just because the above example is small/simple.  I mean, because React devs have been demonizing many of the simple concepts illustrated in this example.  They keep picking at this basic framework until the result is hardly recognizable.  

**Separation of Concerns**

MVC may not be "a thing" much anymore, but it still hangs heavy in many minds.  I've received feedback, from other professional React devs, that an example like the one above violates _separation of concerns_.  Of course, for all the reasons that I outlined above, I think that's flat-out ridiculous.  But nevertheless, many React devs seem to have some kind of fear about putting too much "logic" in any of their components.

The last place I worked, they literally created _two_ components for every _one_.  The first component held the `render()`.  The second one held all of the functions that were used in that component.  They called this sibling component the _dispatcher_.  Then they bound all the functions from the _dispatcher_ to the first component.  And they somehow thought this was a brilliant way to foster _separation of concerns_.  I thought it was abject idiocy.

The more you do to fling these functions into far-off files/directories, the more obtuse you make your app.  And the more difficult you make your troubleshooting. 

The way we build applications today is like building a car and deciding that the engine should be in Chicago, the wheels and driveshaft should be in Atlanta, the gas tank should be in Seattle, and the cabin should be in Dallas.  And then we congratulate ourselves because we have _separation of concerns_.

The problems arise because we all have nightmares of apps that we had to maintain in the distant past.  Horrific "vehicles" that included an engine, a coal-burning power plant, a Victrola record player, a toaster oven, and three broken down analog televisions - all crammed side-by-side in a single file/class/function/component.  And we've been so traumatized by that experience, that now we try to build new cars with _every different part_ flung out to far-off places.  But we rarely stop to think, "Wait a minute.  What are the parts that still _belong_ together, in one place, very near each other?"

**Obsession With "Purity"**

React/JavaScript devs these days are _obsessed_ with the notion of purity.  Pure components.  Pure functions.  Pure dogma.  These devs will gladly chug a pint of bleach - as long as you assure them that it's absolutely _pure_ bleach.

Look, I get it.  As much as you can, it's useful to break down your app into as many "pure" components/functions as possible.  That purity leads to easier testing and fewer bugs.  And the example above is definitely not "pure".

But you can't build anything bigger than a blog demo without eventually having to create some "impure" components/functions.  Your app will need to have _some_ kinda state, and external memory, and _side-effects_.  It'll need to talk to _some_ kinda data store.  And there's no way to do those things without violating the Holy Scripture of Purity.

**The State-Management Nightmare**

One way that devs strive for more "purity" is by chunking some big, heavy, state-management apparatus into their app and then allowing it to handle all that nasty, dirty, impure state/data management stuff.  So they'll take a component like the one above, and when they're done with it, it will basically be left with nothing but the `render()` function.  Then they'll strain an oblique trying to pat themselves on the back because the refactored component is so "pure".  But that's not _purity_.  That's _obscurity_.

Sure, we _could_ handle most of this oh-so-evil logic in _reducers_ and _actions_ and _subscribers_ and all sorts of other state-management constructs.  Then, when we open the code file for this component, we'd be all self-satisfied with its "purity".  But... the component wouldn't make any _sense_.  

With state-management thrown into the gears, you'd open this file and have a hard time figuring out how the counter is set.  Or where it's set.  You'd have to trace that logic through directories/files that "live" nowhere near this one.  And somehow, React devs think that's... a _good_ thing???

**Klasses R Stoopid**

Sooo many React devs nowadays wake up every morning and sacrifice a fatted calf and their first-born child on the Altar of Functions.  They're brainwashed by the React Illuminati that any code with a `class` keyword in it is somehow Evil & Stooopid.  And any code that consists of _only_ functions is Holy & Righteous.

They can rarely articulate any _empirical_ reason why these demonic classes are actually so... "bad".  They just furl their brow, dig their nose, and mutter something about how "Classes are da sux.  And yer stooopid."

It's not that I don't have empathy for the `class` haters.  It's a _big_ word.  It's too confusing for all but the most advanced of programmers.  It's got that "OOP-shtank" all over it.  You can't be expected to put up with code that actually has a `class` keyword in it!  That's just _not fair_!!  You're perfectly within your rights to curl up into the fetal position any time you so-much-as _look_ upon that scary, nasty, horrible `class` keyword.

This is not some diatribe _against_ functions.  Functions are beautiful.  Functions are great.  But in the example above, everything shown there is part of  a single _logical unit_.  We _could_ create a single `counter.js` file that has all of the functions defined on this page, outside a class, but that would only _obfuscate_ the original intent of this **single** component.

What many in the class-hating, function-worshipping crowd seem to miss is that, in this context, the `class` is a logical _namespace_ for all of the data/display/logic that _should_ be tied to the `<Counter>` component.  Yes... you _could_ break that up into a series of loosely-connected functions - but that serves no logical purpose, other than to appease the Function God.

(If you want to get my full breakdown regarding the abject silliness of your `class` hatred, check out this post: https://dev.to/bytebodger/the-class-boogeyman-in-javascript-2949)

**Anything Outside of a Hook is Stoopid**

I won't go into this point in _toooo_ much detail, cuz it's kinda an extension of the previous point about classes-vs-functions.  But nowadays, even if you **LOVE** functions.  And even if you publicly **DENOUNCE** classes.  That's... not good enough for the elitists.  If you haven't spent your nights/weekends/holidays figuring out how _every dang snippet of code_ can be refactored into a **Hook**, then you're just a script kiddie posing as a "real" programmer.

The Hooks crowd feels downright cultish to me.  There are already sooo many examples I've seen - on the interwebs, or in person - where someone takes a class-based component that's supposedly _bad/wrong/evil_, then they refactor it into a Hook _that has just as many LoC - maybe more_, and they feel all self-satisfied, like they've done something special and they deserve a cookie.  And a smiley face.  And a bowl of ice cream, with extra sprinkles on top.

**Loss of Focus**

In the "default" React framework, there's a real _beauty_ in `setState()`.  `setState()` is only designed to work _on the component where it's called_.  In other words, `setState()` is specifically confined to that component's _locus of control_.  Of course, you can pass a state variable down to the descendants.  You can even pass a function that will allow the descendants to _invoke_ a change on that state variable.  But the _actual work_ of updating that state variable is only ever done _inside the component where it resides_.  

This is critical, because state-management tools throw this concept out the window.  And once you throw that concept out the window, you start implementing a whole bunch of clunky constructs (like _reducers_ and _actions_) in an attempt to shove that genie back in the bottle.  

But you don't have to jump through all of those hoops if you keep state where it "belongs" - inside whatever component naturally should control it.  This allows you to keep _all_ of the updates for those state variables in one, logical place.

> All of the obfuscating overhead of tools like Redux comes about because they're trying to recapture the control of state updates _that already existed in React's default model_.  

<br/>

## Conclusion

Despite what this might read like, the fact is that I don't much care if you're using Redux (or other state-management tools) on your projects.  I don't care if you want to split all of these functions off into their own far-flung directories.  I don't care if you think I'm an idiot because I (continue to) commit the sin of using the **_evil_** `class` keyword.  

But so many of these _fads_ that have swept through the React community (and they _are_ fads) have the very tangible effect of degrading what was, originally, a very beautiful framework.  It's only a matter of time before someone comes up with a Hooks replacement, and then they'll be telling you that _you're_ an idiot for using those old, washed-up constructs.  (Even though they won't be able to give you any _empirical_ reason to backup their contentions.)

So much of what made React _amazing_ in the first place has now become rare in "real" React applications.  The React Illuminati have spent so much time trying to craft fixes/replacements for original React features (that were never _broken_ to begin with), that now we have React apps/components that are harder to troubleshoot than spaghettified jQuery apps.  

You can rarely ever just open the code for a component and _see_ what it's doing.  The elitists have flung all of the logic into the dark corners of the application.

I'm not saying that every React component must/should look like the one above.  But the farther we stray from that model, the more we undercut many of the things that made React great in the first place.