Declarative Conditional Wrappers in React
Tags: react, javascript, webdev, tutorial
Published: 2023-01-17 16:02:02

## The Problem
You're defining a `Username` component and, depending on the `pageURL` prop, you either wrap your JSX in an [`anchor`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) element or you don't.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mvrbewscj0xf4cxbnmh2.png)


Sounds simple enough, and with a simple element like our example, it doesn't take that many extra lines.

```js
const Username = ({username, pageURL}) => (
  <div>
    {pageURL 
      ? <a href={pageURL}>{username}</a>
      : username
    }
  </div>
)
```
If our inner JSX gets too unwieldy we can try saving it as its own component or a local variable and use the ternary operator again.

While the ternary operation is declarative and clear, the idea of repeating the same inner elements on both sides of its conditional return begs for a different approach.

_What we want is a way to declaratively wrap JSX elements depending on specific conditions_

## A Solution

When I faced this problem in my own project and set out to solve it, I imagined nested container components similar to React Router. Something that looks like this:

```js
const Username = ({username, pageURL}) => (
  <div>
    <Optional when={pageURL}>
      <a href={pageURL}> // Conditionally rendered
        <Target>
          {username}
        </Target>
      </a>
    </Optional>
  </div>
)
```
I liked this approach a lot, and it definitely felt, very much, within reach.

### The Plan:
1. Define an `Target` component which simply returns its `children`
2. Define an `Optional` component, which finds a nested `Target` component, and depending on its `when` prop, either render just the `Target` or all its `children`.

The tricky part was in step 2, namely _finding_ the nested `Target`. 

To do it, we needed to:

1. Find a way to search through the `Optional` component's children efficiently.
2. Figure a way to identify the `Target` component.

**Searching for the Target**
To achieve **step 1** we'll first note that wrapping elements have a `children` prop which can either be a single child element, or an array of elements.

Efficiently iterating over this element tree depends the problem. For our case, we can nominally expect that our `Target` won't be too deep in our `Optional` frame, **but** our frame can prepend elements or components with deep trees before the `Target` potentially leading us down wasteful rabbit holes. 

So, I chose to do a **breadth first search** to quickly find the ,likely, shallow `Target` without going deep into sibling trees.

**Identifying the Target**
We can't rely on type or constructor names to look for our `Target` because minification or uglification obfuscates that data. We need to manually set a property that our BFS can check for to properly identify the target.

One way to do this, and ultimately, the method I chose, is to simply add a static property to the `Target` component function. We just need to ensure that the property name is unique enough to not interfere with other functionality.

## The Code
```js
function Target({ children }) {
  return children;
}

Target.optName = "Target";
```
```js
function Optional({ when, children }) {
  const target = findTarget(children);

  // Run a BFS to find Target component
  function findTarget(children) {
    let queue = Array.isArray(children) ? [...children] : [children];
    while (queue.length) {
      const current = queue.shift();
      if (current?.type?.optName === "Target") return current // Check static property

      // Add children to queue
      const children = current.props?.children;
      if (!children) continue;
      if (Array.isArray(children)) {
        queue.push(...children);
      } else {
        queue.push(children);
      }
    }
  }
  // Either return everything enclosed or just the Target
  return when ? children : target;
}
```
And that's it. We can now declaratively wrap elements conditionally.

Here's our simple username example:
{% embed https://codesandbox.io/embed/misty-leftpad-xv2u5f?fontsize=14&hidenavigation=1&theme=dark&view=split %}

## Further Discussion
This doesn't have to be the final form of our solution, we can define our target in even more ways and customize our approach.

Below I nested two optional wrappers with the main `Target`, a counter, furthest in. But if we want to be able to keep inner wrapper when we turn off the outer one, we need to make sure _it's_ wrapped in a `Target` component as well.

```js
<Optional when={showOuter}>
  <div className="outer">
    <Target>
      <Optional when={showInner}>
        <div className="inner">
          <Target>
             <Counter />
          </Target>
        </div>
      </Optional>
    </Target>
  </div>
</Optional>
```
This quickly gets out of hand and is pretty ugly to boot. Is there another, more inline way to identify if an element is a `Target`? We can add a prop!

So, instead of wrapping our target elements in a component, we'll signify that its regular container `isTarget` by adding just that prop.

```js
<Optional when={showOuter}>
  <div className="outer">
    <Optional when={showInner} isTarget>
      <div className="inner">
        <Counter isTarget />
      </div>
    </Optional>
  </div>
</Optional>
```
Look much better. Then we simply update our BFS to check each child element for an `isTarget` prop, and pick the first one that has it as our new target.

Here's an example:
{% embed https://codesandbox.io/embed/laughing-robinson-hjnctq?fontsize=14&hidenavigation=1&theme=dark&view=split %}

Realistically, we'd want to choose a more uncommon but still understandable prop if we're to use this approach to avoid interacting with a component's own functionality since we're passing props into them.

**And that's all!** Now we can conditionally wrap elements and components without pesky ternary operators and enjoy a more declarative feel to our JSX.