Keep react component complexity in check with inversion of control
Tags: react, javascript, frontend, beginners
Published: 2024-03-13 02:20:11


I'm sure at one point in time you've found yourself sitting in front of a desk cluttered with random things! how did it get to that point? Slowly but surely, you've been placing more and more stuff on a perfectly tidy desk until it turned into a pain to fix.

This is similar to what happens in React components. We start simple and clean, but as we add more features the code becomes messy and hard to maintain.

Let's see one way we can prevent that.

Starting with a (simplified) confirmation prompt like this one:

```js
const ConfirmationPrompt = () => (
  <div>
     <p>Are you sure?</p>
     <div>
       <button>Cancel</button>
       <button>Confirm</button>
     </div>
  </div>
)

// Used as <ConfirmationPrompt />
```

We later find ourselves asking confirmation for an action that is critical to our system. We can add a warning message to emphasize the importance

```js
const ConfirmationPrompt = ({destructive}) => (
  <div>
     {destructive && (<p>☠️ DANGER ☠️</p>)}
     <p>Are you sure?</p>
     <div>
       <button>Cancel</button>
       <button>Confirm</button>
     </div>
  </div>
)

// Used as
<ConfirmationPrompt destructive />
```

Things seem good, but what if some actions are dangerous **and** need to trigger an email notification? We could just copy-paste the same logic, but notice that we quickly run into one BIG problem:
The possible variations of our component **are growing exponentially**

```js
const ConfirmationPrompt = ({destructive, triggersNotifications}) => (
  <div>
     {triggersNotifications && (<p>ℹ️ Email will be sent</p>)}
     {destructive && (<p>☠️ DANGER ☠️</p>)}
     <p>Are you sure?</p>
     <div>
       <button>Cancel</button>
       <button>Confirm</button>
     </div>
  </div>
)

// Used as 
<ConfirmationPrompt
 destructive
 triggersNotifications
/>
```

Our first example had 1 possible state, the second had 2 (destructive and non-destructive) but the third now has 4!

Remember: <u>patterns stick</u> and <u>code that has changed is prone to keep changing</u>, so it's best to address this early.


Here's where things get interesting. Instead of the component acting like a mind reader, guessing what other parts of the code will need out of the ConfirmationPrompt, we can **flip the script** and make them decide!
This simple and powerful tool is called **inversion of control**. 
Essentially, we're giving back control to the user of our component, making it more flexible and adaptable. 

It looks like this: 

```js
const ConfirmationPrompt = ({header}) => (
  <div>
     {header} //Our caller decides what to put here
     <p>Are you sure?</p>
     <div>
       <button>Cancel</button>
       <button>Confirm</button>
     </div>
  </div>
)

// Used as 
<ConfirmationPrompt
 header={
    <>
       <WillNotifyMessage/>
       <DangerZoneMessage/>
    </>
 }
/>

// or

<ConfirmationPrompt
 header={<WillNotifyMessage/>}
/>

// and any other use case that comes up!
```

Alright! Our original component is kept short and concise while the rest of the codebase is spared the burden of knowing how ConfirmationPrompt should be configured.

Sadly, it won't help us in keeping a clean desk but inversion of control has many applications in React and you'll find examples with different variations. The idea behind it is always the same:

_To give control back to the caller of the function_