DEV Community

Nadeem Khan
Nadeem Khan

Posted on

Creating a Reusable Component in React: Handling Unlimited Future Changes

Hi Folks,

When working with React, creating reusable components is essential for maintaining clean, manageable, and scalable code. One common component that frequently needs to be adaptable and flexible. In this blog post, we’ll explore how to build a reusable component that can handle various configurations and adapt to future changes without becoming overly complex.

Variants of Headers
Let's consider a header component that may need to support different variants. Below is an example of different header styles:

UI/UX: Different Type of Header with variants

Basic Header Component
A typical React header component might look something like this, where props are used to conditionally render elements like the logo, navigation items, search bar, and cart.

React: Basic Header Component

Using props, a developer might set up the header as follows:

React Code: Props Component Overloaded

Usage:

React Code: Component Usage Which has overloaded props

Handling Future Changes
Now, let's consider a scenario where the requirements change. The header needs to include additional elements like a favorites section, user account details, and a top banner with image and text that can be shown or hidden. If we continue using the prop-based approach, the component might look like this:

React Code

As you can see, this approach quickly becomes unwieldy with 10-15 props. Managing such a prop-heavy component can lead to cumbersome and error-prone code.

Using Compound Components
To address this issue, we can use the compound component pattern. This approach allows for more flexible and readable code by enabling the parent component to define the structure and the children to specify the content.

Here's an example of how we can refactor our header component using compound components:

Header.js
React Code: Compound Pattern

Usage
React Code: Compound Pattern

Benefits of Compound Components

Flexibility: Compound components provide greater flexibility as they allow you to nest components and pass props directly to the specific parts of the header.

Readability: The structure is more readable and maintains a clear hierarchy.

Scalability: Adding new components or modifying existing ones becomes easier without making the parent component too complex.

Reusability: Each part of the header can be reused independently in different contexts or layouts.

By using the compound component pattern, we ensure that our header component remains manageable and adaptable to future changes, providing a robust solution for complex UI requirements.

Top comments (9)

Collapse
 
alaindet profile image
Alain D'Ettorre

Although this is nice, you're leveraging a very rare and little bit frowned upon syntax: the subcomponents are functions stored as properties of the main function component. It's ok-ish, but name the one time you've ever used functions properties.

Maybe a better approach would be to export a default object containing all component functions as properties of that object and exporting the main component as any other sub component but maybe with a conventional 'Root' or 'Base' key, so that you'll use Header.Root or Header.Base

It's more verbose but also more obvious and does not use funny syntax

Collapse
 
aminbasiran profile image
aminpls

Could you please share some code or any resources i can refer to. I dont quite understand from the reading. Thank you

Collapse
 
alaindet profile image
Alain D'Ettorre • Edited
// Declare sub-components, each in its file
const Root = () => ...; 
const TopBanner = () => ...;
const Logo = () => ...;
const NavItems = () => ...;
const SearchBar = () => ...;

// index.ts
// Pack the sub-components and re-export them
import { Root } from './Root';
import { TopBanner } from './TopBanner';
import { Logo } from './Logo';
import { NavItems } from './NavItems';
import { SearchBar } from './SearchBar';

const Header = {
  Root,
  TopBanner,
  Logo,
  NavItems,
  SearchBar,
};

export default Header;

// In another external file, import the whole compound component from a single default export
import Header from 'components/Header';

export const MyPage = () => {
  return (
    <>
      <Header.Root>
        <Header.TopBanner>...</Header.TopBanner>
        <Header.Logo>...</Header.Logo>
        <Header.NavItems>...</Header.NavItems>
        <Header.SearchBar>...</Header.SearchBar>
      </Header.Root>
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode
Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
nadeemkhanrtm profile image
Nadeem Khan • Edited
  1. For Example I used this it can be from constant file or from props/dynamic value its totally depend on requirement. For this blog I do not want to make it complex to understand so most of the things assumed as an constant. I will take care your points in the next blog to put it on the starting of the blogs It would be easy for people to understand the concept.

  2. I am agree with your point. It can be any. But for this blog I assumed it can be array for now.

If It is object then we have to use like

React: NavItems

I am creating an navItems for Store only to make it precise and more undertandable.

const navItems = {
'Store': {
'Shop': ['Shop the latest', 'Mac', 'iPad', 'iPhone', 'Apple Watch', 'Apple Vision Pro', 'Accessories'],
'Quick Links: ['Find a store', 'Order Status', 'Apple Trade In', 'Financing'],
'Shop Special Stores': ['Certified Refurbished', 'Education', '', 'Business'],
}
}

// We need to take Object.entries to loop over it.

Object.entries(navItems).map(([key, value], index) => {
    return (
      <div className="sfly-lib-group sfly-lib-pr-6" key={index}>
        <h3>{key}</h3>
        {value && typeof value === 'object' && Object.entries(value).map(([subKey, subValue]) => (
          <div key={subKey}>
            <h4>{subKey}</h4>
            {Array.isArray(subValue) && subValue.map((item, i) => <p key={i}>{item}</p>)}
          </div>
        ))}
      </div>
    );
  });
Enter fullscreen mode Exit fullscreen mode

For Array We can use simple map function as mentioned in Blog itself.
Nadeem Khan.
Thanks.

Collapse
 
newton_urbanetz profile image
Newton Urbanetz

No problem, I appreciate a lot of posts like yours.
My point of view is about the use of optional chaining in this case.
Because if I'm sure it will be an array in my opinion it's not necessary to use it, and second, if I use the optional chaining navItems?.map, but navItems is not an array, it will have an exception, an error TypeError: navItems.map is not a function.
It was the use of optional chaining that I wanted to emphasize :).

Collapse
 
pbnjson profile image
Joshua Clancy

This is amazing. I'm definitely incorporating it from now on.

Collapse
 
nadeemkhanrtm profile image
Nadeem Khan

Thanks for giving me a motivation to do more blogs like this.

Collapse
 
markuz899 profile image
Marco

Great approach! Very useful in the layout phase

Some comments may only be visible to logged-in visitors. Sign in to view all comments.