Conditional Wrapping in React - An Advanced Approach (HOCs & Hooks)
Tags: react, advanced, webdev, javascript
Published: 2022-12-17 23:18:58


In React, there may be times when we have a generic element that needs to be rendered differently based on a specific condition. For example, we might have a component that displays a list of items, and we want to wrap each item in a link element if it has a specific URL. One way to handle this is to use an if-else statement, but this can result in messy and duplicative code.

> Are you a self-taught developer? if so you must read this book: [The Self-Taught Full Stack Web Developer's Journey: A Comprehensive Guide to Success](https://aykacode.teachable.com/p/my-downloadable-22933)

A cleaner and more flexible solution is to use higher order components (**HOC**s) to handle the conditional wrapping for us.
Higher-order components (HOCs) are a pattern in React that allow you to abstract complex behaviors or functionality into reusable functions. They are a way to enhance or modify the behavior of a component without changing its code.

Here's an example of a HOC that adds conditional behavior to a component:

```javascript
const withConditionalWrapper = (condition, Wrapper) => {
  return WrappedComponent => {
    const WithConditionalWrapper = props => {
      return condition ? (
        <Wrapper {...props}>
          <WrappedComponent {...props} />
        </Wrapper>
      ) : (
        <WrappedComponent {...props} />
      );
    };

    WithConditionalWrapper.displayName = `withConditionalWrapper(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;

    return WithConditionalWrapper;
  };
};
```
The `withConditionalWrapper` HOC is a function that takes two arguments: `condition` and `Wrapper`. It returns a function that takes a `WrappedComponent` as an argument. This returned function is a new component called `WithConditionalWrapper`.

The `WithConditionalWrapper` component is a function that takes props as an argument and returns JSX. If the condition is true, the Wrapper component is rendered with the `WrappedComponent` as its child, passing the props to both components. If the condition is false, the `WrappedComponent` is rendered with the props passed to it.

> The `displayName` property is a way to give a component a more descriptive name that can be used for debugging purposes. It is a string that is displayed in the React Developer Tools (a browser extension for debugging React applications) and can make it easier to identify a component in the component tree.

>The `displayName` property is set to a string that includes the name of the HOC and the wrapped component. This can make it easier to understand how the component was created and what it does when debugging the application.

>It is important to note that the `displayName` property is not required for a component to work, and it is only used for debugging purposes. However, it can be a helpful tool when working with large or complex applications.

Now let's look at an example of how this HOC might be used. Here's the Wrapper component:

```javascript
const Wrapper = ({url, children}) => {
 return (
  <a href={url}>
   {children}
  </a>
 );
};
```

The Wrapper component is a simple function that takes an `url` prop and some children, and renders an anchor element with the children as its content and the `url` as its `href` attribute.

Next, we need to define the condition function:

```javascript
const condition = props => !!props.url;
```

The condition function is a simple function that takes props as an argument and returns a boolean value indicating whether the `url` prop is truthy or not.

Now let's look at the component that is going to be wrapped:

```javascript
const MyFunctionalComponent = ({text}) => {
 return (
  <div>
   {text}
  </div>
 );
};
```

The `MyFunctionalComponent` is a simple functional component that takes a text prop and renders it inside a div element.

Finally, let's see how we can use the `withConditionalWrapper` HOC to enhance the `MyFunctionalComponent` with the Wrapper component:

```javascript
const EnhancedComponent = withConditionalWrapper(condition, Wrapper)(MyFunctionalComponent);
```

To use the enhanced component, we can simply render it like this:

```javascript
<EnhancedComponent text="Click here" url="https://www.example.com" />
```

In this example, the `EnhancedComponent` will render the `MyFunctionalComponent` wrapped in the Wrapper component, because the condition function will return true when passed the props {text: "Click here", url: "https://www.example.com"}. The resulting JSX will be equivalent to this:

```javascript
<Wrapper url="https://www.example.com">
  <MyFunctionalComponent text="Click here" />
</Wrapper>
```

On the other hand, if we render the `EnhancedComponent` with props that do not include a truthy `url` prop, the Wrapper component will not be rendered and the `MyFunctionalComponent` will be rendered on its own:

```javascript
<EnhancedComponent text="Click here" />
```

In this case, the resulting JSX will be equivalent to this:

```javascript
<MyFunctionalComponent text="Click here" />
```

As you can see, HOCs provide a powerful way to enhance the behavior of a component without modifying its code. They are a useful tool to have in your React toolkit and can help you write more reusable, modular code.

## using hooks instead of HOCs (very similar)

Here is an example of a hook that serves the same purpose as the `withConditionalWrapper` higher-order component (HOC):

```javascript

const useConditionalWrapper = (condition, Wrapper) => {
  const [renderWrapper, setRenderWrapper] = useState(false);

  useEffect(() => {
    setRenderWrapper(condition());
  }, []);

  return renderWrapper ? Wrapper : ({children}) => <>{children}</> 
};


const LinkWrapper = ({url, children}) => {
  return (
    <a href={url}>
      {children}
    </a>
  );
};


const MyFunctionalComponent = ({text}) => {
  return (
    <div>
      {text}
    </div>
  );
};

```


You can use this hook as follows:

```javascript

const MyComponent = ({url}) => {
  const Wrapper = useConditionalWrapper(() => !!url, LinkWrapper);

  return (
    <Wrapper url={url}>
      <MyFunctionalComponent text="Content goes here" />
    </Wrapper>
  );
};
```