Mapping React Children With Finesse
A useful solution I've found for dynamically adding props and validating implicitly rendered React children.
Gia Thinh Nguyen
Published on
In the early stages of learning TypeScript, I found it difficult to write React the way I used to with good ol' JavaScript, until I started to realize the significant gains I had in code hygiene and readability by using some of these well known patterns.
Working with the implicitly passed children
children in React is a must, and TypeScript can make
this much more explicit and type-safe with this super easy interface provided with the React
namespace.
Of course, PropsWithChildren
is a generic function, so you can add any additional types there.
I was introduced to this pattern early on when building UI components that share a common concern or state, where these components can be written in a way that makes them more closely related. Take for example a typical dropdown menu, it could look like this:
This approach provides the benefit of being able to localize state via the outermost component
Menu
, which acts as a Context
provider, and sharing it to the descendants without having to
pass it along as props.
Writing these components is also quite simple with JSX dot notation which is a vanilla React feature. While dot notation is usually associated with the common way to access object properties in JavaScript, in React it can be quite a handy tool, afterall a React element is an object itself.
Taking the example of a dropdown Menu, we can follow the idea closely with this highly abstracted snippet.
When it came to visually hiding a component, I would often write code that looks like this:
As I came to learn later, this is not the best way especially when you consider performance or accessibility. While it's not a huge hit to performance, hidden DOM nodes can pollute your HTML if the page is already heavy, and for accessibility it is not great to visually hide content that should be accessible to screen readers.
This article provides the best explanation on what approach to use when dealing with visually hidden but accessible components.
Oten times, I start writing type definitions related to the component inside the same file. Some might prefer to keep these definitions in another tidy types-only file because it would be readily available as an export.
But in the case where you do not wish to extend the definition, but merely reuse it, you can easily retrieve the
definition of the component's props defintion using the React.ComponentProps<>
generic class.
While this might seem like a quick shortcut over exporting the type and importing it where you might need it, this approach can be useful when the original type definition is not readily available, while still offering the possibility to extend the definition if need be.
While working with React, I found myself often wishing a certain ComponentA
had the same React API event props
readily available like onMouseOver
or onKeyDown
, especially when it is being used in a different context.
So in this fictional dilemma, where you would want to rely on the well typed onMouseOver
API from React
to add an additional callback onto your button component, you simply can't.
Fortunately, this can be easily achieved by using the previous lesson's React.ComponentProps
generic to
have a wider definition for the component props.
By having a broader definition for the props and by spreading said props into a component, we end up with a versatile and reusable component.
Occasionally, we would like to substitute our underlying React component with an alternative component
based on a certain condition. Take for example a <Button/>
component, perhaps we would like to render
a link in the presence of an href
attribute, and a regular button
when that attribute is not added.
We can take advantage of how JSX tags renderering works, by simply applying a condition to what component gets rendered. Remember that React components are capitalized and must be in-scope, that is they must be declared before being passed into the tags.
While this compiles with JavaScript, this is not acceptable with the type definition we gave to ButtonProps
,
because the attributes for an anchor tag and button tag do not overlap so neatly (easiest example being that
button
does not typically receive the href
attribute).
This is where type guards come in handy, as it allows us to pass the appropriate type definition on props
based
on a condition, which is the presence of href
in the example above.
You can read more about type guards and narrowing prop definitions at the incredibly useful React TypeScript CheatSheet documentation.
And voilà, we have ourselves a versatile component that can used to render links or buttons! While JSX tags are an amazing part of React, we must also be careful in how we use this pattern since it can leave us with un-safe code, and luckily that is where TypeScript can really help out by providing tools that can allow us to use this cool pattern.
Here is a brief summary of each pattern I wrote about.
React.PropsWithChildren<{}>
generic when you need to explicitly type the children
prop.display: none
as a crutch for visually hiding elements.React.ComponentProps
when you need to infer the type definition of a component's props.React and TypeScript offers an incredibly amazing developer experience when authoring UI components and these patterns only offer a glimpse of what can be achieved lately while building apps for the web.