The Splintering Effects of Redux
Tags: react, redux, mvc, javascript
Published: 2020-03-08 08:57:30

I love me some React.  It's become my go-to framework-of-choice for nearly any new dev project.  And my professional life is spent as a mostly-React-centric developer.  But... I also feel that side-effects of React's state-management... _challenges_, coupled with a growing elitism in the React/JavaScript community, has led to a beautiful framework becoming increasingly _splintered_.

Allow me to explain...

## In the Beginning

On the eighth day, The Creator (Jordan Walke) created React.  And it was good.  But almost from the beginning, there was something rotten festering in the Garden of Eden (React).  This rotten apple was the "core" method of handling shared state management.

Specifically, the base/core/out-of-the-box implementation of React specified that shared values would be passed between components through props.  This is (un)affectionately referred to by React devs as _prop drilling_ - the concept that values are "shared" between components by constantly passing them down, from one layer through another and yet another and another (ad nauseum), until some lower-level component finally has access to the raw values (state) or callbacks (functions) that it needs to serve its core function.

Most seasoned devs could read the original spec and think: 

> Mannnn... that will never work for the Crazy Complex Legacy Apps that I'm dealing with.

So... mere seconds after "The Beginning", anyone who started evaluating React began devising "better" ways to share state between components.  

The Prophet (Dan Abramov et. al.) was not oblivious to these concerns.  So even as other developers were trying to develop their own global-state-management solutions, The Prophet gave us: Redux.  And it was... good?

Umm... maybe.  Maybe not.

But we're getting ahead of ourselves.

## The Dark Ages of MVC

I can't attest as to why _you_ might've gotten into React development.  But I can absolutely remember what excited _me_ about the framework.  I saw React as a gorgeous way to circumvent the aging beast known as _MVC_.

For any devs "of a certain age", we can clearly remember a time when you couldn't escape MVC.  Hell... you couldn't even get a job - _any_ dev job - unless you mentioned "MVC" at least a dozen times during your tech interview, and took every opportunity to praise it.  

MVC is no longer the tech flavor _du jour_.  But I feel like its ghost still stalks modern dev teams.  Its _goals_ are still prevalent in any "mature" dev effort today.  Because MVC was a major movement that aimed to codify _seperation of concerns_.  

If you ever worked in an _old_-school server-side language that used no MVC, you understand the benefits of the pattern.  Really _old_ PHP, Perl, VB, or JSP apps would often have _a single page_ of friggin code that would, in one fell swoop, try to do _everything_ that was needed to render that page.  In that single page, you could have HTML output (the View), database queries (the Model), and business logic that would determine which bits to show to the user at any given point (the Controller).  

So back when any "real" programming was done on the server side, MVC was a useful pattern.  You had anything drawn from the data layer (the Model), anything that was sent to the browser (the View), and any business logic that drove what the user should-or-should-not see (the Controller).

And this all made a lot of sense... when the browser was just a dumb client that was rendering whatever was sent down the pipe from the web server.  But then JavaScript had its breakthrough - and all hell broke loose.

## jQuery Distinguished Between _Business_ Logic and _Display_ Logic

Let's be absolutely clear:  There's nothing in jQuery that's inherently _MVC_.  But soooo many MVC apps (before jQuery) tried to treat _everything_ that was sent to the browser as simple, static _display_.  There was no distinction between _business_ logic and _display_ logic.  Under the MVC model, if there was _any_ "logic" to be applied to the page, that logic was supposed to live _in the controller_ (which probably lived somewhere on the server).   

But jQuery challenged that assumption (in a **big** way).  Because then, for the first time, you could write a rich, client-side app with all sorts of fancy "logic" that was completely contained in the display layer (the View).  

I can't speak for anyone else, but I'll admit that this is the first time I started thinking deeply about the difference between _business_ logic and _display_ logic.  Because, in a "standard" MVC paradigm, _all_ of that logic gets shoved into the Controller (which probably resides on the server).  But as client-side applications finally started coming of age, the line between these two types of logic started to blur.  And as they blurred, it became apparent that jQuery wasn't inherently equipped to handle this split.

## The Interim Step of Knockout

React was hardly the first JS framework to provide rich, frontend capabilities that would update the DOM (the View) in real-time.  In fact, the next "leap" forward from jQuery was, IMHO, _Knockout_.  Knockout provided a "magical" feature known as two-way data-binding.  Using Knockout, you could set a variable in one place, then you could update the value _in many different places_, and the UI would "auto-magically" update based upon the new value.

Knockout has, for the most part, fallen by the wayside.  The idea of _two-way data-binding_ has become something of a dirty word amongst many JS devs.  I'll get into this in more detail further down in this post.  But, for the time being, just put a bookmark on this idea as we move along in JS's evolution...

## React to the Rescue

When I first saw React, it legitimately excited me!  It provided an oh-so-elegant model whereby a dev could define all of the _display logic_ that accompanied a given element (i.e., _a component_).  In its "core" implementation, it was very obvious (to me) where _any_ of a component's display logic should "live" - right inside the component itself.

Consider the following example:

```javascript
import React from 'react';

export default class IdealImplementation extends React.Component {
   this.state = { value : '' };

   render = () => {
      return (
         <>
            <div>Type something in this field:</div>
            <input
               onChange={this.updateTextField}
               name={'demoField'}
               value={this.state.value}
            />
         </>
      );
   };

   updateTextField = (event = {}) => {
      const newValue = event.currentTarget.value;
      this.setState({value : newValue});
   };
}
```

In the React ecosystem, it doesn't get much more basic than this.  We have a dirt-simple component that has a basic implementation of an `<input>` field.  The _value_ of that `<input>` field is driven _by its state_.  

I gotta tell you that, as a self-professed "old-school" developer, this just makes sooooo much sense to me.  The "memory" of the `<input>` field - i.e., its _state_ - is saved right in the component itself.  

We're not calling back to the server to inquire about the `<input>` field's state.  We're not dependent upon a new roundtrip call to the web server to tell us how the `<input>` field should be rendered.  It's all managed _right here_ in the display component that's handling (rendering) this component.  _IN_ the display!

Should we be calling out to the web server (or, to another component) to inquire as to how this component should be rendered???  _Of course not._  That would represent a ridiculous "separation of concerns".  The server shouldn't be telling this `<input>` field how to render/act.  Because this `<input>` field is inherently _a display_ component.  This means that any "logic" that drives its presentation is, inherently, _display logic_.  So the logic that tells us how to _display_ this component should be housed - wait for it... _right here_, IN this component.

But the common sense of this approach doesn't stop here.  This component does have some small degree of _state_.  This component has to "remember" something about itself.  Specifically, it has to "remember" what values have already been typed into the `<input>` field. 

So where should that _state_ be stored?  Well... how about, right here, inside the component that houses the `<input>` field itself???

When I type it out this way, it seems _painfully obvious_.  The _display_ logic for this `<input>` field should be housed _right here_, where the `<input>` field is rendered.  And what if the user actually interacts with this field (by typing inside of it)?  Well... once again, that small bit of _state_ should also be housed _right here_, where the `<input>` field is rendered.  If we need to _update_ this value, we should be doing that update _right here_, where the `<input>` field is rendered, via `setState()`.

## The Splintering of Global State Management

Maybe you're nodding along with me and wondering what is the point of this whole post?  React provides a beautiful, _native_ way for components to maintain their own "memory" (_state_).  And it provides a beautiful, _native_ way for us to update those values with `setState()`.  

> So... what's the problem???

The "problem" comes when we decide that we want to _share_ the value of this `<input>` field out to other components.  React _does_ provide a native means to accomplish this - by passing the value down to descendant components via props.  _But_... most professional React devs have come to see this process as unmanageable and un-scaleable in "large-scale" React applications.  In fact, they even created a derogatory term for it: _prop drilling_.

Let's imagine that we have 20 nested, downstream components that all need to "listen" to the value of this `<input>` field as the user types a value into it.  Under React's "core" implementation, this would mean that we'd have to pass the value of the `<input>` field down, via props, _through 20 layers of descendant components_.  

Most of the professional React devs that I know would consider this situation to be unmanageable using "base" React functionality.  The idea of passing a single value, via props, through 20 nested layers, is the kind of challenge that would lead most devs to reach for a global state-management solution.

## A "Solution" With a Whole Host of New Problems

The _prop drilling_ problem is why React devs use a global state-management solution.  There are _many_ of them out there, but the "default" choice is Redux.  Because Redux was crafted by devs who are closely aligned with the team that wrote React. 

In this example, if the `<input>` value must be shared out to many other components, most React devs assume they must use a global state-management solution.  And this is usually: Redux.  Under this scenario, they put the `<input>` field's value into the Redux global store.  Then they can be confident that this same value will be available to any other components that need it - without any of the hassle that's inherent in _prop drilling_.

So if the `<input>` field's value must be shared out to many other components, the "default" answer is to shove the field's `state` value into a _global_ state-management store.  But this is not without side effects...

Remember, above, where I talked about _Knockout_?  Many JS devs grew weary of that library because they had a global variable that was set in one place, but it could be updated _in many places_.  So when they found that their variable somehow ended up with a "wrong" value, it was unduly difficult to trace the origination of that bug, because it was a pain to figure out _exactly where_ the aberrant value had originated.

## Redux Solves a Problem... By Creating Another Problem

The Redux creators knew about the headaches that could arise from true _two-way data binding_.  To their credit, they didn't want to recreate that problem.  They understood that, if you put a variable into a _global_ store, then, in theory, any other component with access to the _global_ store can update that variable.  And if any component accessing the _global_ store can update the variable, you run into the same headaches experienced with Knockout, whereby it can be extremely difficult to track the source of your bug.

So they implemented a framework that requires you to update your global variables through a host of ancillary functions.  There are _reducers_ and _actions_ and _subscribers_.  All of these ancillary constructs are designed to "control" the way that global variables are updated - and to provide single points where any side-effects can be generated.

But the practical effect of these changes is that we export a huge amount of our _display logic_ out into far-flung files/directories.  When you look inside a project that's deeply ingrained in Redux, it can be very confusing to figure out exactly _where_ the changes are being made.

You can have a basic state variable `foo` that is defined in `<SomeComponent>`.  But when you open `<SomeComponent>`, there is little-or-no code _in that component_ that drives the value (or the side effects) of `foo`.  

For this reason, I've found that not only is Redux development a separate skill in itself, but merely _troubleshooting_ Redux apps is also its own separate skill.  It's not sufficient to merely be "a React expert".  If you don't have significant experience troubleshooting _Redux apps_, it can take farrrrr longer than a "Redux developer" to find even the simplest of bugs.

## A Better Way Forward

I won't rehash my previous posts here, but if you look at the other entries in this series, you'll see that I've been spending _a lot_ of time with the Context API.  This hasn't been mere intellectual curiosity.  I've been doing this because I find Redux's core framework to be an unnecessary abstraction.

When you use the Context API (or React's "default" prop drilling approach), it preserves soooo much of React's original _beauty_.  Via the Context API, I can share functions/variables out to the rest of my app.  _But_... I can also ensure that those variables are only ever updated in a single place.  Specifically, I can keep the `state` of _any_ component - big or small - confined to the original component where that state "lives". And I can do it all with React's native `setState()` approach.  

When I'm using the Context API, I can open a component and see whatever `state` variables are defined for that component - right there, _in that component_.  If those variables ever need to be updated, I can also see _all_ of the functions/side-effects that affect those variables - right there, _in that component_.  

I don't have to trace logic through far-flung `actions` and `reducers` that live in scantly-associated directories.  If a component has a state variable of `foo`, then I can confidently open _that single, simple component_ to see **all** of the ways in which `foo` can theoretically be changed.  I can pass _functions_ that allow other components to update `foo`, but the actual **work** of updating `foo` is always done in one, logical place - in the same component where `foo` was originally defined.