React Design Patterns Part Two: Props Combination
Complexity level: Beginner
In the previous post we covered the conditional rendering where we looked at the different ways to conditionally render our react components and what are the pros and cons of rendering inside vs outside of a component. Next up, we will be looking into the props combination pattern and how we can use this pattern to make our components use less props.
What is the props combination pattern?
Props are used to pass data from one component to another. The prop combination pattern groups related props into a single object. This object is then passed as a single prop to a component.
Some benefits of this pattern include reduction of boilerplate code, improving code readability and maintainability.
Let’s take a look at an example:
const CardComponent = ({
title,
subText,
ctaText,
ctaUrl,
imageAltText,
imageUrlMobile,
imageUrlTablet,
imageUrlDesktop,
containerClassName,
titleClassName,
subTextClassName,
ctaClassName,
}) => <article>{/* Lots of imaginary code here... */}</article>;
Yes… this is pretty wild but it’s very common to see this in the real world. What we have going into this component is:
- image — the image sources, imageAltText
- cta — text and ctaUrl
- content — title, sub title
- classNames — all classNames
Now look at this when it is grouped:
const CardComponent = ({ title, subText, cta, image }) => (
<article>{/* Lots of imaginary code here... */}</article>
);
It is now a lot simpler to understand, we know the component has a title, it also needs a cta and an image. If we then wanted to, we would add some styles in case we need to tweak the card.
Can you put functions inside of an prop object?
In theory, yes but only if you use memoization on your functions to prevent any re-renders on the react component. Each time a component re-renders, the function is re-created and when it is passed down into a component it will be a new instance of a function which will force the react component to re-render. For example:
const Component = () => {
const onFooBar = () => {
// A new instance of me is created when Component re-renders
}
return <ComponentTwo actions={{ onFooBar, }} />
}
Using useCallback hooks in react enables the ability for these functions to not be called again unless a dependency changes. For example:
const Component = () => {
const onFooBar = useCallback(() => {
// A new instance of me is created when Component re-renders
}, []);
return <ComponentTwo actions={{ onFooBar, }} />
}
Should you do this for every function? Probably not, React is extremely performant when it comes to re-rendering but if you want to you can useCallback.
Conclusion
- Use an object when you want to encapsulate and organise related functions or reduce the number of props, but consider memoizing to avoid unnecessary re-renders.
- Pass functions separately if the component API is simple, or if passing an object would add unnecessary complexity.