Why Is This An "Anti-Pattern" in React???
Tags: react, redux, javascript, tutorial
Published: 2020-02-21 07:35:12


When I first started learning React, I had some initial confusion.  In fact, I think that almost _anyone_ who's done React wrestles with the same questions.  I know this, because people have built entire libraries to address these concerns.  Two of these main issues, that seem to strike nearly every budding React dev, are:
<br/>

1. "How does one component access the information (especially, a state variable) that resides in another component?"<br/><br/>

1. "How does one component invoke a function that resides in another component?"<br/><br/>

JavaScript devs, in general, (and React devs, in particular) have become ever-more focused in recent years on writing "pure" functions.  Functions that aren't intertwined with state changes.  Functions that don't require outside connections to databases.  Functions that don't require knowledge of anything happening outside the function.  

A focus on "pure" functions is certainly a noble goal.  But if you're building any application of a reasonable size and scope, there's just no way to make every function "pure".  At some point, it becomes ridiculous to architect an application where at least _some_ of the components are not inherently aware of what some of the _other_ components in the application are doing.  These strands of interconnectedness are commonly known as _dependencies_.  

In general, dependencies are a _bad_ thing, and it's wise to introduce them only when necessary.  But again, if your app has grown to a "certain size", it's inevitable that at least _some_ of your components will be be dependent upon one another.  Of course, the React devs understood this, so they provided a basic means by which one component could pass critical information, or functions, down to its children.  

## The Default Approach of Passing Values by Props

Any state value can be passed to another component by props.  Any functions can be passed down via those same props.  This gives child components a way to become "aware" of state values that are stored higher up the chain.  And it also gives them the potential to invoke actions on the parent components.  This is all fine-and-good.  But it doesn't take long before new React devs start to worry about a specific, potential "problem".

Most apps are built with some degree of "layering".  In larger apps, this layering can be quite deeply-nested.  A common architecture might look something like this:

 1. `<App>`&rarr; calls &rarr;`<ContentArea>` 
 2. `<ContentArea>`&rarr; calls &rarr;`<MainContentArea>` 
 3. `<MainContentArea>`&rarr; calls &rarr;`<MyDashboard>` 
 4.  `<MyDashboard>`&rarr; calls &rarr;`<MyOpenTickets>` 
 5. `<MyOpenTickets>`&rarr; calls &rarr;`<TicketTable>` 
 6. `<TicketTable>`&rarr; calls a series of &rarr;`<TicketRow>`s
 7. Each `<TicketRow>`&rarr; calls &rarr;`<TicketDetail>` 

In theory, this daisy chain could go on for many more levels.  All the components are part of a coherent whole.  Specifically, they're part of a hierarchy.  But here's the key question:
<br/>

> In the example above, can a `<TicketDetail>` component read the state values that are in `<ContentArea>`?  Or... can a `<TicketDetail>` component invoke functions that reside in `<ContentArea>`?

<br/>The answer to both questions is, yes.  In theory, all of the descendants can be aware of all variables stored in their ancestors.  And they can invoke the functions of their ancestors - with one big caveat.  In order for that to work, those values (either state values or functions) must be explicitly passed down _as props_.  If they aren't, then the descendant component has no awareness of the state values, or functions, that are available on the ancestor.  

In minor apps or utilities, this may not feel like much of a hurdle.  For example, if `<TicketDetail>` needs to query the state variables that reside in `<TicketRow>`, all that must be done is to ensure that `<TicketRow>`&rarr; passes those values down to &rarr;`<TicketDetail>` in one-or-more props.  The same is true if `<TicketDetail>` needs to invoke a function on `<TicketRow>`.  `<TicketRow>`&rarr; would just need to pass that function down to &rarr;`<TicketDetail>` as a prop.  The headache occurs when some component waaayyy down the tree needs to interact with the state/functions that otherwise live much high-up in the hierarchy.  

The "traditional" React approach to that problem is to solve it by passing the variables/functions all the way down through the hierarchy.  But this creates a lot of unwieldy overhead and a great deal of cognitive planning.  To do this the "default" way in React, we would have to pass values through many different layers, like so:

`<ContentArea>` &rarr; `<MainContentArea>` &rarr; `<MyDashboard>` &rarr; `<MyOpenTickets>` &rarr; `<TicketTable>` &rarr; `<TicketRow>` &rarr; `<TicketDetail>`

That's a _lot_ of extra work just so we can get a state variable from `<ContentArea>` all the way down to `<TicketDetail>`.  Most senior devs quickly realize that this would create a ridiculously-long chain of values and functions constantly getting passed, through props, through a great many intermediary levels of components.  The solution feels so needlessly clunky that it actually stopped me from picking up React the first couple of times that I tried diving into the library.

## A Giant Convoluted Beast Named _Redux_

I'm not the only one who thinks it's highly impractical to pass _all_ of your shared state values, and _all_ of your shared functions, through props.  I know this, because it's nearly impossible to find any sizable React implementation that doesn't also make use of a bolted-on appendage known as a state-management tool.  There are many out there.  Personally, I _love_ MobX.  But unfortunately, the "industry standard" is Redux.  

Redux was created by the same team that built the core React library.  In other words, the React team made this beautiful tool.  But almost immediately realized that the tool's inherent method for sharing state was borderline unmanageable.  So if they didn't find some way to work around the inherent obstacles in their (otherwise beautiful) tool, it was never going to gain widespread adoption.  

So they created Redux.  

Redux is the mustache that's painted on React's Mona Lisa.  It requires _a ton_ of boilerplate code to be dumped into nearly all of the project files.  It makes troubleshooting and code-reading far more obtuse.  It sends valuable business logic into far-off files.  It's a bloated mess.  

But if a team is faced with the prospect of using React + Redux, or using React _with no third-party state-management tool at all_, they will almost _always_ choose React + Redux.  Also, since Redux is built by the core React team, it carries that implicit stamp of approval.  And most dev teams prefer to reach for any solution that has that kind of implicit approval. 

Of course, Redux also creates an underlying web of _dependencies_ in your React application.  But to be fair, _any_ blanket state-management tool will do the same. The state-management tool serves as a common store in which we can save variables and functions.  Those variables and functions can then be used by any component with access to the common store.  The only obvious downside, is that now, every component is _dependent_ upon that common store.

Most React devs I know have given up on any Redux resistance they initially felt.  (After all... _resistance is futile_.)  I've met plenty of guys who outright _hated_ Redux, but faced with the prospect of using Redux - or not having a React job - they took their _soma_, drank their Kool-Aid, and now they've just come to accept that Redux is a necessary part of life.  Like taxes.  And rectal exams.  And root canals. 

## Rethinking Shared Values in React

I'm always a little too stubborn for my own good.  I took one look at Redux and knew that I had to look for better solutions.  I _can_ use Redux.  I've worked on teams where it was used.  I understand what it's doing.  But that doesn't mean that I enjoy that aspect of the job.  

As I've already stated, if a separate state-management tool is absolutely needed, then MobX is about, oh... a million times better than Redux.  But there's a deeper question that really _bothers_ me about the hive-mind of React devs:<br/><br/>

> Why are we constantly reaching for state-management tools in the first place??

<br/>You see, when I first started React development, I spent a number of nights at home playing around with alternative solutions.  And the solution I found is something that many other React devs seem to scoff at - but they can't really tell me _why_.  Let me explain:

In the putative app that was outlined above, let's say that we create a separate file that looks like this:

```javascript
// components.js
let components = {};
export default components;
```

That's it.  Just two little lines of code.  We're creating an empty object - _a plain ol' JavaScript object_.  Then we're setting it up as the `export default` in the file.

Now let's see what the code might look like inside the `<ContentArea>` component:

```javascript
// content.area.js
import components from './components';
import MainContentArea from './main.content.area';
import React from 'react';

export default class ContentArea extends React.Component {
   constructor(props) {
	  super(props);
	  components.ContentArea = this;
   }
   
   consoleLog(value) {
      console.log(value);
   }
   
   render() {
      return <MainContentArea/>;
   }
}
```

For the most part, this looks like a fairly "normal" class-based React component.  We have a simple `render()` function that's calling the next component below it in the hierarchy.  We have a little demo function that does nothing but send some value to `console.log()`, and we have a constructor.  But... there's something just a _little bit different_ in that constructor.

At the top of the file, notice that we imported that super-simple `components` object.  Then, in the constructor, we added a new property to the `components` object with the same name as `this` React component.  In that property, we loaded a reference to `this` React component.  So... from here out, anytime we have access to the `components` object, we'll also have _direct access_ to the `<ContentArea>` component.

Now let's go way down to the _bottom_ of the hierarchy and see what `<TicketDetail>` might look like:

```javascript
// ticket.detail.js
import components from './components';
import React from 'react';

export default class TicketDetail extends React.Component {
   render() {
      components.ContentArea.consoleLog('it works');
      return <div>Here are the ticket details.</div>;
   }
}
```

So here's what's happening.  Every time the `<TicketDetail>` component is rendered, it will call the `consoleLog()` function that exists _in the `<ContentArea>` component_.  **Notice** that the `consoleLog()` function was _not_ passed all the way through the hierarchy chain via props.  In fact the `consoleLog()` function was not passed _anywhere_ - _at all_ - to _any_ component. 

And yet, `<TicketDetail>` is still capable of invoking `<ContentArea>`'s `consoleLog()` function because two necessary steps were fulfilled:

 1. When the `<ContentArea>` component was loaded, it added a reference to itself into the shared `components` object.

 2. When the `<TicketDetail>` component was loaded, it imported the shared `components` object, which meant that it had _direct access_ to the `<ContentArea>` component, even though `<ContentArea>`'s properties were never passed down to `<TicketDetail>` through props.

This doesn't just work with functions/callbacks.  It can also be used to directly query the value of state variables.  Let's imagine that `<ContentArea>` looks like this:

```javascript
// content.area.js
import components from './components';
import MainContentArea from './main.content.area';
import React from 'react';

export default class ContentArea extends React.Component {
   constructor(props) {
	  super(props);
	  this.state = { reduxSucks:true };
	  components.ContentArea = this;
   }
   
   render() {
      return <MainContentArea/>;
   }
}
```

Then we can write `<TicketDetail>` as so:

```javascript
// ticket.detail.js
import components from './components';
import React from 'react';

export default class TicketDetail extends React.Component {
   render() {
      if (components.ContentArea.state.reduxSucks === true) {
         console.log('Yep, Redux is da sux');
      }
      return <div>Here are the ticket details.</div>;
   }
}
```

So now, every time `<TicketDetail>` is rendered, it will look to see the value of `<ContentArea>`'s `state.reduxSucks` variable.  And, if the value is `true`, it will `console.log()` the message.  It can do this even though the value of `ContentArea.state.reduxSucks` was never passed down - to _any_ component - via props.  By leveraging one, simple, base-JavaScript object that "lives" outside the standard React life cycle, we can now empower any of the child components to read state variables _directly_ from any parent component that's been loaded into the `components` object.  We can even use that to invoke a parent's functions in the child component.

Because we can directly invoke functions in the ancestor components, this means that we can even influence parent state values directly from the child components.  We would do that like this:

First, in the `<ContentArea>` component, we create a simple function that toggles the value of `reduxSucks`.

```javascript
// content.area.js
import components from './components';
import MainContentArea from './main.content.area';
import React from 'react';

export default class ContentArea extends React.Component {
   constructor(props) {
	  super(props);
	  this.state = { reduxSucks:true };
	  components.ContentArea = this;
   }
   
   toggleReduxSucks() {
      this.setState((previousState, props) => {
         return { reduxSucks: !previousState.reduxSucks };
      });
   }
   
   render() {
      return <MainContentArea/>;
   }
}
```

Then, in the `<TicketDetail>` component, we use our `components` object to invoke that method:

```javascript
// ticket.detail.js
import components from './components';
import React from 'react';

export default class TicketDetail extends React.Component {
   render() {
      if (components.ContentArea.state.reduxSucks === true) {
         console.log('Yep, Redux is da sux');
      }
      return (
         <>
		    <div>Here are the ticket details.</div>
		    <button onClick={() => components.ContentArea.toggleReduxSucks()}>Toggle reduxSucks</button>
	     </>
      );
   }
}
```

Now, every time the `<TicketDetail>` component is rendered, it will give the user a button.  Clicking the button will actually update (toggle) the value of the `ContentArea.state.reduxSucks` variable in real-time.  It can do this even though the `ContentArea.toggleReduxSucks()` function was never passed down through props.  

We can even use this approach to allow an _ancestor_ component to directly call a function _on one of its descendants_.  Here's how we would do that:

The updated `<ContentArea>` component would look like this:

```javascript
// content.area.js
import components from './components';
import MainContentArea from './main.content.area';
import React from 'react';

export default class ContentArea extends React.Component {
   constructor(props) {
	  super(props);
	  this.state = { reduxSucks:true };
	  components.ContentArea = this;
   }
   
   toggleReduxSucks() {
      this.setState((previousState, props) => {
         return { reduxSucks: !previousState.reduxSucks };
      });
      components.TicketTable.incrementReduxSucksHasBeenToggledXTimes();
   }
   
   render() {
      return <MainContentArea/>;
   }
}
```
And now we're going to add logic in the `<TicketTable>` component that looks like this:

```javascript
// ticket.table.js
import components from './components';
import React from 'react';
import TicketRow from './ticket.row';

export default class TicketTable extends React.Component {
   constructor(props) {
	  super(props);
	  this.state = { reduxSucksHasBeenToggledXTimes: 0 };
	  components.TicketTable = this;
   }
  
   incrementReduxSucksHasBeenToggledXTimes() {
      this.setState((previousState, props) => {
         return { reduxSucksHasBeenToggledXTimes: previousState.reduxSucksHasBeenToggledXTimes + 1};
      });      
   }

   render() {
      const {reduxSucksHasBeenToggledXTimes} = this.state;
      return (
         <>
		    <div>The `reduxSucks` value has been toggled {reduxSucksHasBeenToggledXTimes} times</div>
		    <TicketRow data={dataForTicket1}/>
		    <TicketRow data={dataForTicket2}/>
		    <TicketRow data={dataForTicket3}/>
	     </>
      );
   }
}
```
And finally, our `<TicketDetail>` component remains unchanged.  It still looks like this:

```javascript
// ticket.detail.js
import components from './components';
import React from 'react';

export default class TicketDetail extends React.Component {
   render() {
      if (components.ContentArea.state.reduxSucks === true) {
         console.log('Yep, Redux is da sux');
      }
      return (
         <>
		    <div>Here are the ticket details.</div>
		    <button onClick={() => components.ContentArea.toggleReduxSucks()}>Toggle reduxSucks</button>
	     </>
      );
   }
}
```

Now, you may spot something odd about these three classes.  In our application hierarchy, `<ContentArea>`&rarr; is an ancestor of &rarr;`<TicketTable>`&rarr; which in turn is an ancestor of &rarr;`<TicketDetail>`.  This means that when `<ContentArea>` is mounted, it will (initially) have no "knowledge" of `<TicketTable>`.  And yet, inside `<ContentArea>`'s `toggleReduxSucks()` function, there's an implicit call to a _descendant's_ function: `incrementReduxSucksHasBeenToggledXTimes()`.  So this will **break**, _right_???

Umm... no.

You see, given the layers that we've created in the app, there is only one "path" through the app in which `toggleReduxSucks()` can be called.  It goes like this:

 1. `<ContentArea>` is mounted-and-rendered.

 2. During this process, a reference to `<ContentArea>` is loaded into the `components` object.

 3. This eventually leads to `<TicketTable>` being mounted-and-rendered.

 4. During this process, a reference to `<TicketTable>` is loaded into the `components` object.

 5. This eventually leads to `<TicketDetail>` being mounted-and-rendered.

 6. The user is then shown the 'Toggle reduxSucks' `<button>`.

 7. The user clicks the 'Toggle reduxSucks' `<button>`.

 8. This calls the `toggleReduxSucks()` function that lives in the `<ContentArea>` component.

 9. This, in turn, calls the `incrementReduxSucksHasBeenToggledXTimes()` function in the `<TicketTable>` component.

 10. This _works_ because, by the time the user has a chance to click the  'Toggle reduxSucks' `<button>`, a reference to the `<TicketTable>` component will have already been loaded into the `components` object.  And when `<ContentArea>`'s `toggleReduxSucks()` function is called, it will be able to find a reference to `<TicketTable>`'s `incrementReduxSucksHasBeenToggledXTimes()` function in the `components` object.

So you see, by leveraging the inherent hierarchy of our application, we can place logic in the `<ContentArea>` component that will effectively call a function in one of its descendant components, _even though the `<ContentArea>` component wasn't yet aware of the `<TicketTable>` component at the time that it was mounted_.  

## Throwing Out Your State-Management Tools

As I've already explained, I believe - _deeply_ - that MobX is _vastly superior_ to Redux.  And whenever I have the (rare) privilege of working on a "green fields" project, I will always lobby hard for us to use MobX rather than Redux.  But when I'm building _my own apps_, I rarely (if ever) reach for _any_ third-party state-management tool at all.  Instead, I frequently use this uber-simple object/component-caching mechanism wherever it's appropriate.  And when this approach simply doesn't fit the bill, I often find myself reverting to React's "default" solution - in other words, I simply pass the functions/state-variables through props.

## Known "Issues" With This Approach

I'm not claiming that my idea of using a basic `components` cache is the end-all/be-all solution to every shared-state/function problem.  There are times when this approach can be... _tricky_.  Or even, downright _wrong_.  Here are some notable issues to consider:

* **This works best _with singletons_.**<br/>For example, in the hierarchy shown above, there are zero-to-many `<TicketRow>` components inside the `<TicketTable>` component.  If you wanted to cache a reference to _each_ of the potential `<TicketRow>` components (and their child `<TicketDetail>` components) into the `components` cache, you'd have to store them in an array, and that could certainly become... _confusing_.  I've always avoided doing this.<br/><br/>

* **The `components` cache (obviously) works on the idea that we can't leverage the variables/functions from other components unless we know that they've already been loaded into the `components` object.**<br/>If your application architecture makes this impractical, this could be a poor solution.  This approaches is ideally suited to Single Page Applications where we can know, with certainty, that `<AncestorComponent>` _will always_ be mounted before `<DescendantComponent>`.  If you choose to reference the variables/functions in a `<DescendantComponent>` directly from somewhere within an `<AncestorComponent>`, you must ensure that the application flow would not allow that sequence to happen until the `<DescendantComponent>` is already loaded into the `components` cache.<br/><br/>

* **Although you can _read_ the state variables from other components that are referenced in the `components` cache, if you want to _update_ those variables (via `setState()`), you must call a `setState()` function that lives in its associated component.**

## Caveat Emptor

Now that I've demonstrated this approach, and outlined some of the known restrictions, I feel compelled to spell out one major caution.  Since I've "discovered" this approach, I've shared it, on several different occasions, with people who consider themselves to be certified "React devs".  Every single time that I've told them about it, they always give me the same response:<br/><br/>

> Umm... Don't do that.

<br/>They wrinkle their nose and furrow their brow and look like I just unleashed a major fart.  Something about this approach just seems to strike many "React devs" as being somehow... _wrong_.  Granted, I have yet to hear _anyone_ give me any _empirical_ reason why it's (supposedly) "wrong".  But that doesn't stop them from treating it like it's somehow... _a sin_.

So even if you _like_ this approach.  Or maybe you see it as being somehow "handy" in given situations.  I wouldn't recommend _ever_ pulling this out during a job interview for a React position.  In fact, even when you're just _talking_ to other "React devs", I'd be careful about how/if you choose to mention it at all.  

You see, I've found that JS devs - and React devs, in particular - can be incredibly _dogmatic_.  Sometimes they can give you _empirical_ reasons why Approach A is "wrong" and Approach B is "right".  But, more often than not, they tend to just view a given block of code and declare that it's somehow "bad" - even if they can't give you any substantive reason to back up their claims.

## Why, Exactly, Does This Approach Irk Most "React Devs"???

As stated above, when I've actually shown this to other React colleagues, I've yet to receive any _reasoned_ response as to why this approach is "bad".  But when I _do_ get an explanation, it tends to fall into one of these (few) excuses:

* **This breaks the desire to have "pure" functions and litters the application with tightly-coupled dependencies.**
OK...  _I get that_.  But the same people who immediately dismiss this approach, will happily drop Redux (or MobX, or _any_ state-management tool) into the middle of nearly all of their React classes/functions.  Now, I'm not railing against the general idea that, sometimes, a state-management tool is absolutely beneficial.  But _every_ state-management tool is, essentially, a giant dependency generator.  Every time you drop a state-management tool into the middle of your functions/classes, you're essentially littering your app with dependencies.  Please note:  I didn't say that you should drop _every one_ of your functions/classes into the `components` cache.  In fact, you can carefully choose _which_ functions/classes are dropped into the `components` cache, and _which_ functions/classes try to reference something that's been dropped into the `components` cache.  If you're writing a pure _utility_ function/class, it's probably a very poor idea to leverage my `components` cache solution.  Because using the `components` cache requires a "knowledge" of the other components in the application.  If you're writing the kind of component that should be used _in many different places of the app_, or that could be used _across many different apps_, then you absolutely would **not** want to use this approach.  But then again, if you're creating that kind of global-use utility, you wouldn't want to use Redux, or MobX, or _any_ state-management tool inside the utility either.<br/><br/>

* **This just isn't "the way" that you do things in React.  Or... This just isn't _industry-standard_.**<br/>Yeah... I've gotten that kinda response on several occasions.  And quite frankly, when I get that response, it makes me lose a little bit of respect for the responder.  I'm sorry, but if your only excuse is to fall back on vague notions of "the way", or to invoke the infinitely-malleable boogeyman of "industry standards", then that's just _fuckin lazy_.  When React was first introduced, it didn't come "out of the box" with _any_ state-management tools.  But people started playing with the framework and decided that they needed additional state-management tools.  So they built them.  If you really wanna be "industry standard", just pass _all_ of your state variables and _all_ of your function callbacks through props.  But if you feel like the "base" implementation of React doesn't suit 100% of your needs, then stop closing your eyes (and _your mind_) to any out-of-the-box thinking that isn't personally approved by Dan Abramov.  

## So What Say _YOU_???

I put up this post because I've been using this approach (in my personal projects) for years.  And it's worked _wonderfully_.  But every time I step out of my "local dev bubble" and try to have an intelligent discussion about it with other, _outside_ React devs... I'm only met with dogma and mindless "industry standard" speak.  

Is this approach truly _bad_???  Really.  I _want_ to know.  But if it's really an "anti-pattern", I'd sincerely appreciate if someone can spell out some _empirical_ reasons for its "wrongness" that go beyond "this isn't what I'm _accustomed_ to seeing."  I'm open-minded.  I'm not claiming that this approach is some panacea of React development.  And I'm more-than-willing to admit that it has its own limitations.  But can _anyone_ out there explain to me why this approach is just outright _wrong_???

I'd sincerely love any feedback you can provide and I'm genuinely looking forward to your responses - even if they're blatantly critical.