This is something you do not always need, but I wrote this article for those looking for it.
Sometimes we might have a generic element, a specific component that renders inside a modal.
When a specific flag is set, the component should get a parent wrapper to display it in a different variant.
We could use an if...else statement, but it looks messy.
Conditional wrapping in React
Let's say we got specific service cards to make it a bit easier to follow. In some cases, they explain a service, but in others, they need to link to a detail page.
The component might look like this.
const ServiceCard = ({ title, description, image, url }) => {
return (
<section>
<h2>{title}</h2>
<p>{description}</p>
<img src={image} alt={title} />
</section>
);
};
As mentioned, what happens if we need to wrap this whole thing in a link element when the URL exists?
We could use the if...else loop.
const ServiceCard = ({ title, description, image, url }) => {
return (
<section>
{url ? (
<a href={url}>
<h2>{title}</h2>
<p>{description}</p>
<img src={image} alt={title} />
</a>
) : (
<>
<h2>{title}</h2>
<p>{description}</p>
<img src={image} alt={title} />
</>
)}
</section>
);
};
But this shows duplicate code, so it's a bit silly. If we need to style each element, we must modify it in two places.
So how can we better wrap this conditionally?
We can create a generic component that handles this for us, the component will be named ConditionalWrapper
, and it will take a condition, the wrapper, and the children it should wrap.
const ConditionalWrapper = ({ condition, wrapper, children }) =>
condition ? wrapper(children) : children;
With that in place, we can use it on our existing component to clean it up.
const ServiceCard = ({title, description, image, url}) => {
return (
<section>
<ConditionalWrapper
condition={url}
wrapper={children => <a href={url}>{children}</a>}
>
<>
<h2>{title}</h2>
<p>{description}</p>
<img src={image} alt={title} />
</>
</ConditionalWrapper>
</section>
)
}
And now, if we use our component, depending on whether we pass the URL. It will render with or without the href. And the best part is that we have no duplication in our elements.
For example, the following use case:
<ServiceCard title='test' description='foo bar' img='img1.jpg' />
It would return the following HTML output:
<section>
<h2>test</h2>
<p>foo bar</p>
<img src="img1.jpg" alt="test" />
</section>
We will get the following output if we put the URL in the element.
<section>
<a href="url">
<h2>test</h2>
<p>foo bar</p>
<img src="img1.jpg" alt="test" />
</a>
</section>
Pretty cool, right?
The main magic, of course, happens in the ConditionalWrapper component and, to be precise, the wrapper argument.
Since we pass the children (which is a React default prop), we can see that the use case of our function as in wrapper={children => <a href={url}>{children}</a>}
states.
- If the condition is met
- Wrap the children in this specific element
There will only be a handful of times when you might need this function, but it can be a huge lifesaver.
Note: big thanks to Olivier for the original idea!
Thank you for reading, and let's connect!
Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter
Top comments (43)
Great idea, Chris & Olivier!
This becomes even more powerful if you make it an array of
(wrapper | false)[]
, so you can express multiple conditional wrappers in a more readable (less deep) structure:This allows you to use
P.S.: Wrote that on my cellphone, so this is untested.
Sir you are a legend.
Was this you ? π
Nope, I used a normal cellphone. I don't think it would work, had I written it on that.
Oh like that idea! π
I just realized this could be made even simpler using Array.prototype.reduce:
Definitely thought-provoking. An abstraction like
ConditionalWrapper
component is one way of doing it in this simplified use case. But it may need serious consideration in the real setting. How about refactoring the duplicated part into a separate component e.g.ServiceCardDetails
while keeping the JSX expression?Abstractions are nice but duplication is far superior if the abstraction does not withstand real challenges. Only you as an engineer can tell if such abstraction will continue to deliver value in the long-run.
Yeah kind of depends on the use-case sometimes your scenario might be the better abstraction.
Some times it might be the ConditionalWrapper.
It comes down to how the abstraction takes place.
Aren't we overthinking this, doesn't this works fine? (Not react dev here)
Not sure how my example is complicated to be honest?
Seems pretty straightforward.
My point was that it seemed like an unnecessary abstraction since the "wet" code is not really a bother to use, you understand?
Not that your code was complicated by any means
Yes Even I am thinking the same....what's the need to complicate....even this keeps our code DRY
The only code that really matter is this piece:
So seems pretty simple right?
The other is just examples of the use-case really.
Hope that clarifies some things π
Why it couldn't be as simple as this below ?
This is kind of the extended write down of the function I described.
So if that's easier for you that will work.
Thanks for this really good piece of code π
Question: When you use Conditionalwrapper inside Servicecard, is the closing
</a>
right or should it be</>
?Ah sorry my bad, should have been the fragment closing π
Hi @julia
I believe that might be a typo from the author end,
</>
should be used.Hi Chris,
I really enjoyed your article on using ConditionalWrapper for conditional wrapping in React. In my recent blog post, I introduce an advanced approach using higher order components (HOCs). I found this method to be more flexible and customizable, and I think it might be useful for others looking to add additional functionality to their wrapped elements. Thank you for sharing your technique, and I hope my approach might be of interest to your readers as well.
Thanks for that, help me understand though what's the difference between my generic component and your HOC?
It's just terminology, right?
In a sense, the terms "generic component" and "HOC" are just labels that we use to describe different types of components in software development. However, there are some practical differences between these two types of components that go beyond just the terminology.
An HOC is a function that takes a component and returns a new, enhanced component. This allows you to reuse code and abstract common logic across multiple components, and can be a powerful tool for creating reusable abstractions in your code.
On the other hand, a generic component is simply any component that is not an HOC. It could be a presentational component that only handles the rendering of UI elements, or a container component that handles the data management and behavior of your application.
So while the terms "generic component" and "HOC" are just labels, they do reflect some practical differences in how these components are used and what their purpose is.
I created a library that solves these kind of problems: npmjs.com/package/react-jsx-flow
Thats something handy π
Awesome! I'll go check it out
I feel this is similar with Mojo::Template.
FYI
github.com/aliakakis/react-templat...
github.com/aliakakis/react-templat...
Looks like a clean implementation. I will try it out later.