Find & Filter React Children By Type

Take control of your children in React for all environments

Michael Paravano
9 min readOct 2, 2020
Photo by Joshua Eckstein

Article Update:

I’ve decided rewrite these utils from the ground up, add a bunch of new ones (including deep/recursive searching) and publish an NPM package for all to consume:

This article will discuss the how-it-works for finding and filtering React children by type as it pertains to custom component children. If you are looking at finding and filtering core HTML Element (JSX Intrinsic Element) children like divs, spans, etc, please use react-nanny or see my other article for the how-it-works:

This article will discuss finding and filtering React children by type as it pertains to core custom component children. If you are looking at finding and filtering core HTML Element (JSX) children like divs, spans, etc, please see my other article:

There are situations in which knowing the type of each child passed to your component would be incredibly useful. For example, you might want to:

  • Validate that the consumer provides markup that you expect
  • Conditionally show or hide child items
  • Simplify the use of your component by allowing your consumer to pass in several children, but you want to place one of a certain type in a different location of your output JSX than the rest

The task seems like it should be easy enough accomplish. After all, if you were to console.log(children), you’d see there’s a type key on each child. An internet search will uncover that you can easily do this:

React.Children.toArray(children).map(x => console.log(x.type.name));

Let’s say we need to create a List component that accepts severalToDo components as children like this:

And we define our components like this:

We map over the children in List.jsx just like our internet search told us. Then we run our app and see the following in the console:

ToDo
ToDo
ToDo

We got exactly what we were looking for so we begin coding away… validating here; conditionally showing/hiding there. Our PR gets merged and we think to ourself, “Self, you really nailed it today.” However, we’re about to be in for a big surprise: It doesn’t work like we expect in production.

So what’s the problem?

If we were to run the same map and console.log in production, we’d see something like this:

u
u
u

It turns out that our app’s build has been optimized for production which includes… wait for it… minification! All of our component names and types have now been minified to something that is completely unpredictable that we can’t code against.

Out of the darkness comes a solution!

We can take advantage of the fact that literal string values do not get minified. Simply add a prop that you don’t advertise in your documentation and treat it as a constant. I’ve named it __TYPE in the example below, but you can name it whatever you like. Then give it a default value by defining it via PropTypes.

If we were to now rewrite that mapping that we did at the top of this article to this:

React.Children.toArray(children).map(x => console.log(x.props.__TYPE));

We would get this result in our console in all environments:

ToDo
ToDo
ToDo

I know what you’re thinking because I can read your thoughts, “What’s stopping the consumer from doing something like the following…?”

<ToDo __TYPE="MoreLikeToDontAmirite?" />

The truth is that there really isn’t anything stopping the consumer from doing that. However, we’ve done two things that should immediately discourage people from doing this:

  1. The prop name starts with not one, but two underscores which should indicate that this is definitely a private prop.
  2. The prop name is in all caps which should indicate that it is a constant.

Of course we can and should do more.

We can take advantage of the fact that we can create a custom PropType that will notify the user with an in-your-face console error should they attempt to stray from the default:

If we now update the ToDo.jsx component to consume this custom prop validator like this:

…we should see the following in our console if we try to pass in a value for __TYPE in our App.jsx:

Hijacking __TYPE results in a console error

Validate Your Children

The List.jsx component, as it stands, can accept any child you throw into it, but that’s not desired behavior. For example, we don’t want someone to be able to pass in a div:

We cannot stand for this kind of thing!

Bad React child!

Now that we can identify our children, we create another util to handle this situation for us. Consider the following:

We feed the getChildrenByType function our children and an array of the types we want to include and the function will return only the children that have a matching __TYPE.

The typeOfComponent helper function will check for our __TYPE under the hood. If that isn’t defined, it will next check the stringified type of the component which is helpful for finding HTML element children (i.e. divs, spans, etc.). If you’re interested in filtering those kinds of children, you can find the link to my other article at the top. Otherwise, you can ignore the details of type for now.

That means we can update our List.jsx component with this function:

When we run the updated code, we’ll notice that our list is nice and clean without the bogus div our consumer so carelessly injected.

Bad React child has been removed

Note: If we’re using react-nanny, we can alternatively pass in the actual imported component as part of our types array if it’s in scope:

import ToDo from './components/ToDo;
...
<ul>{getChildrenByType(children, [ToDo])}</ul>

Notice ToDo isn’t a string like it was before. However, if you don’t have your component in scope, you’ll definitely want to key off of a prop value like __TYPE and use a string value in your array.

Conditionally Show/Hide Specific Children

Sometimes you may want to show or hide specific children or children of a certain type based on the configuration of the parent component.

To illustrate this, let’s create a new type of ToDo called ToDoCompleted which adds “- COMPLETED” to the end of the item:

Next, we can add a new prop to our List.jsx component called hideCompleted which will conditionally hide or show completed todo items in the list. We can also conditionally add the ToDoCompleted type to the array that we’re passing to our getChildrenByType util function:

If we now update our App.jsx to this:

…and start our app, we will see this:

Item 2 is completed and our bogus div is removed

If we were to add the hideCompleted prop to our list like this:

<List hideCompleted>

…we will see Item 2 removed from the list:

Items which have been completed are no longer displayed

Move Your Children Around

Let’s say that the design team comes to us and says that all of the app’s todo lists should have completed items at the bottom of the list when they are to be displayed. We can accomplish this in List.jsx with our same util function:

In the code above, we’re finding all ToDo children and all ToDoCompleted children and rendering each of those out instead of all children like we were before. However, if someone were to want the completed items hidden, they can still do that.

If we start up our app, we should see Item 2 at the bottom:

Completed children are at the bottom of the list

Why not simply use render props?

I’m glad you asked. I am not anti-render prop. In fact, I use them quite frequently, but they aren’t a golden hammer solution for every problem.

If you take our previous scenario with being required to move completed items to the bottom of the list, that was not an initial requirement. It was a requirement that was brought to us after the component already existed.

In this case, we could refactor the component to accept a prop called renderCompleted that returns the completed items and we can invoke that function in the spot in our render markup where we want those items to be, but we will be breaking the props api contract which will necessitate action from all consumers.

If the component source code lives in our app, we’d have to refactor every instance that it’s used. If the component is part of a distributed package, we’d have to publish a new major version which consumers would manually need to update in addition to refactoring every used instance. Meanwhile, you have some teams using the new and some using the old which can create a strange experience from the user as they use your product.

In situations like that, it’s better to use this method and herd the children where they need to be. No breaking change; no consumer refactoring required.

Speaking of render props (since you brought it up)…

These techniques aren’t just for children, they also work with render props or any JSX for that matter.

Let’s say you have a component with a render prop called renderActionArea and you expect that prop to return you one or morePrimaryButton components. How do you know the consumer is returning a PrimaryButton and not a div or a span or a SecondaryButton?

Well, now you can! Simply…

const actionArea = 
getChildrenByType(renderActionArea(), ['PrimaryButton']);

Awesome! Do you have any other helpful utils?

Yes! To recap the article update posted at the top, I’ve published an NPM package that has these utils re-engineered to handle additional situations and offer more options to give you flexibility. There are also many additional utils that we didn’t discuss in this article:

Here is a list of the utils currently available in react-nanny:

  • getChild — Gets first child by specified predicate
  • getChildDeep — Gets first child by specified predicate (deep search)
  • getChildByType — Gets first child by specified type
  • getChildByTypeDeep — Gets first child by specified type (deep search)
  • getChildren — Gets all children by specified predicate
  • getChildrenDeep — Gets all children by specified predicate (deep search)
  • getChildrenByType — Gets all children by specified type
  • getChildrenByTypeDeep — Gets all children by specified type (deep search)
  • noEmptyChildrenDeep — Ensure that there is some level of content and not just a bunch of empty divs, spans, etc (deep search)
  • removeChildren — Removes all children by specified predicate
  • removeChildrenDeep — Removes all children by specified predicate (deep search)
  • removeChildrenByType — Removes all children by specified type
  • removeChildrenByTypeDeep — Removes all children by specified type (deep search)
  • typeOfComponent — Gets the string type of the component if defined by a prop, the string type of the core html (JSX Intrinsic) element, or the function type

Go forth and be good to your React children!

As a matter of practice, I highly recommend creating your own prop to identify what kind of component it is. Even if you think you’ll never use it, it’s good to have it in place for a time when it might save your bacon.

If you’re needing to also filter core HTML Element components like divs, spans, etc., I highly recommend you continue on to my article on that topic:

--

--

Michael Paravano

Senior Front-end Engineer specializing in all things front-end software development.