Writing re-usable code with React Composition

In software development, code reusability is a key factor that can save developers a lot of time and effort. With reusable code, developers can easily reuse code blocks and components across multiple projects, which allows us to focus on other important tasks.

Let’s see, how React Composition can help us achieve code reusability.

What is React Composition?

React Composition is a technique that involves building complex components by combining smaller, reusable components.

Imagine that we have a pile of Lego blocks in front of us and we want to build a house. Instead of starting with a single large block, we would start by combining smaller blocks to create the different parts of the house, such as the walls, roof, windows, and doors. Each of these parts is a component that can be combined with other components to build the entire house.

Source: Unsplash

Practical Scenarios

We can create small, reusable components that can be combined together to create more complex components and use them across all projects which can save us time.

Here are a few practical examples:

1. Button Component:

We can create a Button component that can be used throughout our application. This Button component might consist of smaller components such as a Text component and an Icon component, which are combined to create the Button component.

2. Form component:

Similar to button components, we can create reusable form components like Input, Checkbox, and Select. These can be composed together to create more complex forms, and the individual components can be reused in other forms as well.

3. Layout components:

Instead of creating a layout for each page or component in your application, we can create reusable layout components like Header, Footer, and Sidenav. These can be composed together to create a consistent layout across your entire application.

What is Prop drilling and how to fix it using React Composition?

Prop drilling is a common problem in React where data needs to be passed through several layers of components as props, even if those intermediate components don’t actually use the data.

With Composition, we can avoid prop drilling by passing props only to the components that need them. Just by doing a little code restructure, we can avoid the need to pass data through all the intermediate components. This makes our code easier to read, easier to maintain, and faster to render.

Alex Sidorenko has given a wonderful example showing how React Composition can help us solve prop drilling.

To understand this concept in detail we will compare the example of Dialog with and without Composition.

In this code-sandbox, we have 2 files - AppWithoutComposition.js and AppWithComposition.js.

In AppWithoutComposition.js, we are passing props from the EditDialog => Dialog => DialogFooter component. Over time this can become complex, cluttered, and error-prone if we want to add some more data.

To fix these issues, we restructured our components in AppWithComposition.js to make them independent from their surroundings using the children prop.

We modified EditDialog, Dialog, and DialogFooter components to accept children props. From the parent component AppWithComposition passed the data to these components. This way we can use these components anywhere without modifying them which leads to reusability.

import { useState } from "react";

export default function App() {
  const [open, setOpen] = useState(true);

  if (!open) {
    return (
      <button
        onClick={() => setOpen(true)}
        className="text-sm px-4 py-2 bg-blue-500 text-white font-bold rounded"
      >
        Edit dialog
      </button>
    );
  }

  const onClose = () => {
    setOpen(false);
  };

  return (
    <EditDialog>
      <Dialog>
        <dialog open={open} className="rounded p-4 pt-8 w-96 ">
          <div>
            <label
              for="first_name"
              class="block mb-2 text-sm font-medium text-gray-900"
            >
              Name
            </label>
            <input
              type="text"
              id="first_name"
              className="bg-gray-50 border border-gray-300 text-gray-900 rounded-lg block  p-2.5 "
              value="Saeloun"
            />
          </div>
          <DialogFooter>
            <button
              type="button"
              onClick={onClose}
              className="text-sm px-4 py-2 bg-blue-500 text-white font-bold rounded"
            >
              Close
            </button>
          </DialogFooter>
        </dialog>
      </Dialog>
    </EditDialog>
  );
}

function Dialog({ children }) {
  return <>{children}</>;
}

function DialogFooter({ children }) {
  return (
    <div className="mt-8 rounded-b text-right -m-4 p-4" method="dialog">
      {children}
    </div>
  );
}

function EditDialog({ children }) {
  return <>{children}</>;
}

Clean and simple, right?

We have achieved two use cases by breaking down a big component into smaller, reusable components.

Conclusion

React composition is an excellent technique for achieving code reusability in React applications. By breaking down complex components into smaller, reusable components, we can create more maintainable and scalable code.

It can help us avoid prop drilling by breaking down our UI into smaller components and composing them together in a way that minimizes the need for prop drilling.

Need help on your Ruby on Rails or React project?

Join Our Newsletter