React Component Design
Published on
Recently I have been working through a course called Joy of React by Josh W Comeau and thought it might be useful to share some of the component design patterns I learned from module 4. The examples are using a common toast component that is often used to give feedback to users after an action has taken place.
Polymorphism
If you're not already familiar with polymorphism in software, it is where a variable, function or object is given the ability to take on multiple forms.
In the sandbox below, you will see the polymorphism is happening on the Icon variable.
Depending on the variant property passed to the component it will evaluate to a different svg icon from the react-feather package.
Dynamic Styles
The previous example you saw that the four variants were all the one colour. But it would be better if we could have some dynamic styles dependent on the variant property.
In the Toast.module.css I've got css classes by the same name as the variant property that map to
different css variable colours.
In the tsx I'm simply selecting those classes dynamically with styles[variant] which is the same as doing styles.notice, styles.warning and so on.
Rest Operator
The rest operator is almost exactly the opposite of the spread operator but looks similar. It essentially will hold the remaining properties of an array or object within an array. You can read more about it here and here.
In the sandbox below, I’ve extended ToastProps with the IconProps interface so we can pass any of the svg attributes we want to set on the icon and then I’m holding them in the ...rest property at the end when destructing the props of the component.
I then pass {...rest} as a parameter to our polymorphed <Icon />.
In App.tsx, I’m passing some svg attributes for each of the variant examples.
But wait a minute…why doesn’t the size I’m passing to the error variant work?
This is happening because I purposefully placed {...rest} before size={24} in Toast.tsx.
It’s only taking the last declaration of the size property, but you could move the default size of 24 to before ...rest and this would allow the consumer(you, the developer) to pass whatever size they want.
What matters here is how much permission you want the consumer of your component to have!
Provider Pattern
Within React, you get access to the context api which is commonly used when you find yourself doing too much prop drilling and needing to share state between multiple components. Its important to be conscious when using this api about what you hold in your context as you could end up causing unnecessary rerenders for child components that don’t necessarily need access to the data.
Now, lets consider a scenario with this toast component that we want to stack multiple toasts and give the user the ability to acknowledge them by dismissing each one through the cross button. In the sandbox below, I’ve introduced a couple of new files,
ToastProvider.tsx, this is the context provider wrapper that holds all the functional logic and state.
ToastPlayground.tsx, this is the child that has a form that triggers a toast on submit.
ToastShelf.tsx, this iterates the toasts array held in the context to create multiple toasts.
If you open the preview you can enter a message, select a variant and click “Pop toast” and you should see it pop up in the bottom right. You’ll be able to dismiss the toast by clicking the cross button and it will disappear because you’ve acknowledged it.
In this post, we've covered valuable React design patterns, including polymorphism, dynamic styles, the rest operator, and the provider pattern, all using a reusable toast component as an example. These patterns help create flexible, maintainable components that can easily be extended and customized. If you're looking to deepen your React knowledge, I highly recommend Josh W Comeau’s Joy of React course.