DEV Community

loading...
Cover image for Are you giving the proper sense to React.Fragment?

Are you giving the proper sense to React.Fragment?

codbugs profile image Coding Bugs Updated on ・4 min read

UPDATE: Title has been changed because of the misunderstanding around it and the content. I hope this change will cover the expectations and keep healthy conversations to make us keep growing as developers, coders, and professionals in the end.

In my experience, more than 15 years of coding, programming, architecting, I've found people implementing code with no rules or standards and, sometimes, people believing they follow some rules but, in reality, they are not applying by themselves. I was in that situation a lot of times and keep being sometimes as well. I've written this article to show what I think is a good practice and makes us being good professionals.

The issue

The following React code renders a list of items in case of the array passed has them.

function List(props) {  
  const items = props.items;

  return <ul>
    {items && items.map(i => <li key={i}>{i}</li>)}
  </ul>;
}

function App() {
    const collection = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
    return <List items={collection} />;
}

ReactDOM.render(App(), document.getElementById('app'));
Enter fullscreen mode Exit fullscreen mode

Do you think there is something wrong with the code? This code works perfectly fine, it creates a li node with the number as the value.

What happens if the array has no items in it? The ul node will be rendered as well but there won't be any li node and no items in there. This is not a big issue but something not completely well.

We can modify the code in this way:

function List(props) {  
  const items = props.items;

  // *** notice how the code is modified and complex increases
  return items && 
        <ul>
        { items.map(i => <li key={i}>{i}</li>) }
      </ul>;
}

function App() {
    const collection = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
    return <List items={collection} />;
}

ReactDOM.render(App(), document.getElementById('app'));
Enter fullscreen mode Exit fullscreen mode

Like the previous case, this code is working fine, it does what it is supposed to do.

So, let me ask the same question as before, what happens if the array has no items in it?

In this case, a false value and no HTML node are returned. The issue here is that we return different kinds of items depending on the items property passed.

Why is this an issue? In fact, this is just a concept issue more than a coding issue. Returning the same kind of item in this function will make it easier for testing, make it easier to maintain, make it easier to read because it will be consistent and other methods calling this one will receive exactly what they expect and won't have the necessity to check if retrieves a boolean or a component.

The next code modification must have in mind this premise so it'll be like the following:

function List(props) {  
  const items = props.items;

  // check if items is not empty
  const isEmpty = !(items && items.length > 0);

  // always return a React component
  return isEmpty
    ? <React.Fragment />
    : (<ul>
        { items.map(i => <li key={i}>{i}</li>) }
      </ul>);
}

function App() {
    const collection = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
    return <List items={collection} />;
}

ReactDOM.render(App(), document.getElementById('app'));
Enter fullscreen mode Exit fullscreen mode

It seems that our code makes more sense now, doesn't it? We always return a component for any of the options or paths our code takes. If items is not empty, a ul node is returned plus a li node per item inside, a React component in fact.

If there is no items, a React.Fragment component is returned. Both of them are React components, no need for callers to check it.

As a professional programmers that we are, we must give meaning and name our objects.

You may noticed we have few objects here in our code, an empty list component, an item list component, an item component and a manager component. Each of them with a unique responsibility (following the Single Responsibility principle that I'll talk in a future article) and a simple code easy to understand, to maintain and to test.

function ListItem(props) {
    return <li>{props.value}</li>;
}

function ItemList(props) {  
  const items = props.items;

  return <ul>
    { items.map(i => <ListItem key={i} value={i} />) }
  </ul>;
}

// As suggested by Andy Nicholson
const EmptyList = () => <React.Fragment />;

function ListManager(props) {
    const items = props.items;

  const isEmpty = items && items.length <= 0;

  return isEmpty 
    ? <EmptyList />
    : <ItemList items={items} />;
}

function App() {
    const collection = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
    return <ListManager items={collection} />;
}

ReactDOM.render(App(), document.getElementById('app'));
Enter fullscreen mode Exit fullscreen mode

I think this code looks like much better than the first one. Reviewers'll see the person behind the code thought how to face the problem, the constraints, and the paths to take around of it.

Wrapping up

As I wrote some paragraphs above, professional programmers must give meaning to the code. Coding can be done by anyone, programming in a good way with sense are just a subject of professionals.

What do you think about the exercise made in this article?
Hope this can be useful to you or just have fun reading it.

Discussion (28)

Collapse
aleksandrhovhannisyan profile image
Aleksandr Hovhannisyan

The title is clickbait and React.Fragment (<></>) has very valid use cases. The example you showed technically doesn't need to return a fragment—you can just do return !isEmpty && (...). Or just return null if it's empty, depending on what your preference is.

There are other valid use cases for fragments, too, like if you need to return two siblings but without introducing unnecessary wrapper parents.

Collapse
codbugs profile image
Coding Bugs Author

My apologies if you feel the title is clickbait. That wasn't my intention at all.

My article wants to point out that we should use the tools provided by frameworks and libraries in the scope of our domain solution. Returning React.Fragment as an empty list is something valid and will work but it doesn't mean nothing if we, as programmers, don't give it a meaning. Everything depends on requirements and, maybe, we don't need to deep dive in a big file structure with lots of components because it increase complexity.

Collapse
anicholson profile image
Andy Nicholson • Edited

Your final code reads very well, that’s to be commended.

I don’t agree that the solution to every React problem is more components, though. Writing a ListManager component seems like massive overkill and poor naming: what you're describing is more like a ListItemManager - and what is a ListItemManager if not, well, a List?

Naming an empty list is great, but you don’t need a component for that either! You could do just as well by interpolating a const instead, saving yourself a function call:

function List(props) {  
  const items = props.items;
  const emptyList = <React.Fragment />;
  const hasItems = items && items.length > 0;

  return hasItems
  ?  <ul>{ items.map(i => <li key={i}>{i}</li>) }</ul>
  : emptyList
  ;
}
Enter fullscreen mode Exit fullscreen mode

React is awesome, but moar components aren’t always the answer.

Collapse
jackritenour profile image
jackritenour

Very good points.

Collapse
imervinc profile image
👺Mervyn • Edited

IMO:
If your gonna return an empty fragment you might as well return null.
And if your goal is readability, you can always use multiple return like this

function List(props) {  
  const items = props.items;

  // check if items is not empty
  const isEmpty = !(items && items.length > 0);

   if(isEmpty) return null

  return(
      <ul>
        { items.map(i => <li key={i}>{i}</li>) }
      </ul>
      );
}
Enter fullscreen mode Exit fullscreen mode
Collapse
andrewbridge profile image
Andrew Bridge

This is exactly what I came to the comments hoping to post, thanks for getting there first!

I'm baffled to see such a simple example turned into such a lot of code and it exemplifies the over-engineering the React community appears to deal with. Replace null with <React.Fragment /> if you must but creating a list shouldn't take 10s of lines of code.

Collapse
kmturley profile image
Kim T

Multiple returns which both return the same type (html) is the most readable and reusable

Collapse
sergeysova profile image
Sergey Sova

I prefer this over returning fragment.
But in the most situations we need an empty state component instead of null

Collapse
mattcoady profile image
Matt Coady

Null is more performant too. With null there's nothing for react to process, it just moves on. A fragment has to be processed, even if it isn't very much.

Collapse
cullophid profile image
Andreas Møller • Edited

I much prefer this version:

return items && 
        <ul>
        { items.map(i => <li key={i}>{i}</li>) }
      </ul>;
Enter fullscreen mode Exit fullscreen mode
  • The logic is inlined so you don't have to jump between component when reading the code.

  • You won't have ever new dev on your team asking you why you are returning and empty fragment.

The final example looks more "clean", but what actually happens is that you have to jump around in the file a lot to understand what is happening.

Collapse
codbugs profile image
Coding Bugs Author

Agree with your last point. We should balance the benefits of creating components and get a complex solution from a file structure point of view.

Collapse
cullophid profile image
Andreas Møller

And we need to stop promoting ideas like DRY and Single Responsibility Principle, which are at best pointless and at worst harmful.

Collapse
thebarefootdev profile image
thebarefootdev

This example is rather too involved. Returning null for an empty list is perfectly good practice and would provide a testable output. Above you have probably focused too much on the code and not on readability or extensibility certainly not on the presentation to the user.

Unless specified in requirements an returning only if the list has items is a perfectly good option unless there is some feedback needed. It’s not always good to provide feedback to all edge cases to users.

In this sense the type and style of coding very much depends on the domain solution in current construction.

I do agree standards and styling are import to an extent but your example is a little derived. Finally, react fragments have a specific usage for the DOM tree and should not be discarded as readily as you have suggested

Collapse
codbugs profile image
Coding Bugs Author

Totally agree with the requirements specification and domain model comments. My intention in this article is to show an example of how framework features have to be adapted to our solution and not in the opposite.

Collapse
pracoon profile image
Prasham Ashesh

I wish one day I'd be able to express my, points in such linear and organized way!
Good read! definitely scales me up!

Collapse
codbugs profile image
Collapse
pcjmfranken profile image
Peter Franken

Fragments were introduced as a workaround to JSX not supporting unwrapped sibling elements.

Professional programmers should probably start by reading the docs.

Collapse
dmauldin profile image
Dave Mauldin

Full agree. Also, if the docs were read in this case, one would also see that the official advice is to return false ala return condition && <li>item</li> so that React immediately knows to not render anything. Not sure where the expectation that the component must return something renderable came from in this article.

Collapse
willsmart profile image
willsmart

I'm confused 🤔
Do you mean to say you're a coder but don't do it professionally?
Like, if you were a professional programmer you wouldn't have returned a fragment (since they don't do that sort of thing) but would have just returned null and been done with it?

Anyway, I totally agree that to be effective as a coder you need to have all the tools in the toolkit at your disposal, and know the situations where they're useful.

To me, fragments' domain of usefulness is for encapsulating multiple elements, or unknown numbers of elements, as one return value. If I'm returning zero elements, I'll return null since that is what null means: a typed but missing value.

Collapse
anxinyang profile image
AnxinYang

Fragment is very useful when you need to have a parent that does not need to render a container but need to render its children.
From example the Context Provider, component from react-router-dom.

Collapse
leischj profile image
leischj • Edited

I'm so confused. The title of the article says professionals never return "React.Fragment" but by the end of the article you have a component (EmptyList) that returns "React.Fragment" Does this mean you're not a professional?

In your first edit you have a comment to point out the added complexity, but the final edit has a ton of needless complexity, but is missing that comment.

Returning null for an empty list is perfectly testable, acceptable, and most importantly, simple. All things being equal, I'll opt for simplicity.

Collapse
doabledanny profile image
Danny Adams

Makes a lot of sense, thanks for writing and sharing this!

Collapse
codbugs profile image
Collapse
geewhizbang profile image
Geoffrey Swenson

Is this just an example of how irritating React is that you have to worry about all sorts of arcane stuff like this?

I'm using Vue 3 right now really liking the way it handles things more elegantly.

Collapse
jackritenour profile image
jackritenour • Edited

Good article. You make some good points. Now, I want to offer an opinion. It's not an attack on people, it's just that we haven't done a good job with standards with js. Just my observation over the last few years -- take it for it's worth.

A big problem with the js frameworks is that you have very experienced js framework developers who have not worked much with other languages and ecosystems.

Java and Python (especially Python) have very well thought-out and implemented standards. Developers coming from these areas to work on the js framework side typically bring that mentality and discipline with them.

It's a pity that even as long as Node has been around there still is not much of a formal standard for writing Javascript. There's still too much focus on the frameworks themselves instead of on the basics like algorithms and writing good readable code. It's as though most js developers still have not gotten that message.

Collapse
codbugs profile image
Coding Bugs Author

Good comment. I think in the same way. In my case, I've been doing applications with different programming languages: C#, javascript, PowerShell, and Python as examples, and found the same issues in the end, difficulty to solve, maintain or evolve functionalities when there is no standard applied.

Collapse
brian_clark_8cba6546bd8db profile image
Comment marked as low quality/non-constructive by the community. View Code of Conduct
Brian Clark • Edited

If you're going to write an article criticizing the way people code, you might want to develop some standards of your own for writing in English. This article is barely readable.

Forem Open with the Forem app