Find & Filter React Children By Type
Take control of your children in React for all environments
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:
- The prop name starts with not one, but two underscores which should indicate that this is definitely a private prop.
- 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:
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!
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.
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:
If we were to add the hideCompleted
prop to our list like this:
<List hideCompleted>
…we will see Item 2 removed from the list:
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:
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 predicategetChildDeep
— Gets first child by specified predicate (deep search)getChildByType
— Gets first child by specified typegetChildByTypeDeep
— Gets first child by specified type (deep search)getChildren
— Gets all children by specified predicategetChildrenDeep
— Gets all children by specified predicate (deep search)getChildrenByType
— Gets all children by specified typegetChildrenByTypeDeep
— 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 predicateremoveChildrenDeep
— Removes all children by specified predicate (deep search)removeChildrenByType
— Removes all children by specified typeremoveChildrenByTypeDeep
— 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: