A Practical Guide to Creating Reusable React Components


A Practical Guide to Creating Reusable React Components

Although React is one of the most popular and most used front-end frameworks in the world, many developers still struggle when it comes to refactoring code for improved reusability. If you’ve ever found yourself repeating the same snippet of code all throughout your React app, you’ve come to the right article.

In this tutorial, you’ll be introduced to the three most common indicators that it’s time to build a reusable React component. Then we’ll go on to look at some practical demos by building a reusable layout and two exciting React hooks.

By the time you’ve finished reading, you’ll be able to figure out by yourself when it’s appropriate to create reusable React components, and how to do so.

This article assumes a basic knowledge of React and React hooks. If you want to brush up on these topics, I recommend you to check out “Getting Started with React” guide and “Intorduction to React Hooks”.

Top Three Indicators of a Reusable React Component

First let’s look at some indications of when you might want to do this.

Repetitive creation of wrappers with the same CSS style

My favorite sign of knowing when to create a reusable component is the repetitive use of the same CSS style. Now, you may think, “Wait a minute: why don’t I simply assign the same class name to elements that share the same CSS style?” You’re absolutely right. It’s not a good idea to create reusable components every time some elements in different components share the same style. In fact, it may introduce unnecessary complexity. So you have to ask yourself one more thing: are these commonly styled elements wrappers?

For example, consider the following login and signup pages:

// Login.js
import './common.css';

function Login() {
  return (
    <div className='wrapper'>
      <main>
        {...}
      </main>
      <footer className='footer'>
        {...}
      </footer>
    </div>
  );
}
// SignUp.js
import './common.css';

function Signup() {
  return (
    <div className='wrapper'>
      <main>
        {...}
      </main>
      <footer className='footer'>
        {...}
      </footer>
    </div>
  );
}

The same styles are being applied to the container (the <div> element) and the footer of each component. So in this case, you can create two reusable components — <Wrapper /> and <Footer /> — and pass them children as a prop. For example, the login component could be refactored as follows:

// Login.js
import Footer from "./Footer.js";

function Login() {
  return (
    <Wrapper main={{...}} footer={<Footer />} />
  );
} 

As a result, you no longer need to import common.css in multiple pages or create the same <div> elements to wrap everything.

Repetitive use of event listeners

To attach an event listener to an element, you can either handle it inside useEffect() like this:

// App.js
import { useEffect } from 'react';

function App() {
  const handleKeydown = () => {
    alert('key is pressed.');
  }

  useEffect(() => {
    document.addEventListener('keydown', handleKeydown);
    return () => {
      document.removeEventListener('keydown', handleKeydown);
    }
  }, []);

  return (...);
}

Or you can do it directly inside your JSX like this, as is demonstrated in the following button component:

// Button.js
function Button() {
  return (
    <button type="button" onClick={() => { alert('Hi!')}}>
      Click me!
    </button>
  );
};

When you want to add an event listener to document or window, you’d have to go with the first method. However, as you may have already realized, the first method requires more code with the use of useEffect(), addEventListener() and removeEventListener(). So in such case, creating a custom hook will allow your components to be more concise.

There are four possible scenarios for using event listeners:

  • same event listener, same event handler
  • same event listener, different event handler
  • different event listener, same event handler
  • different event listener, different event handler

In the first scenario, you can create a hook where both the event listener and the event handler are defined. Consider the following hook:

// useEventListener.js
import { useEffect } from 'react';

export default function useKeydown() {
  const handleKeydown = () => {
    alert('key is pressed.');
  }

  useEffect(() => {
    document.addEventListener('keydown', handleKeydown);
    return () => {
      document.removeEventListener('keydown', handleKeydown);
    }
  }, []);
};

You can then use this hook in any component as follows:

// App.js
import useKeydown from './useKeydown.js';

function App() {
  useKeydown();
  return (...);
};

For the other three scenarios, I recommend creating a hook that receives an event and an event handling function as props. For example, I will pass keydown and handleKeydown as props to my custom hook. Consider the following hook:

// useEventListener.js
import { useEffect } from 'react';

export default function useEventListener({ event, handler} ) {
  useEffect(() => {
    document.addEventListener(event, props.handler);
    return () => {
      document.removeEventListener(event, props.handler);
    }
  }, []);
};

You can then employ this hook in any component as follows:

// App.js
import useEventListener from './useEventListener.js';

function App() {
  const handleKeydown = () => {
    alert('key is pressed.');
  }
  useEventListener('keydown', handleKeydown);
  return (...);
};

Repetitive use of the same GraphQL script

You don’t really need to look for signs when it comes to making GraphQL code reusable. For complex applications, GraphQL scripts for a query or a mutation easily take up 30–50 lines of code because there are many attributes to request. If you’re using the same GraphQL script more than once or twice, I think it deserves its own custom hook.

Consider the following example:

import { gql, useQuery } from "@apollo/react-hooks";

const GET_POSTS = gql`
  query getPosts {
    getPosts {
    user {
      id
      name
      ...
      }
      emojis {
         id
         ...
      }
      ...
  }
`;

const { data, loading, error } = useQuery(GET_POSTS, {
  fetchPolicy: "network-only"
});

Instead of repeating this code in every page that requests posts from the back end, you should create a React hook for this particular API:

import { gql, useQuery } from "@apollo/react-hooks";

function useGetPosts() {
  const GET_POSTS = gql`{...}`;
  const { data, loading, error } = useQuery(GET_POSTS, {
    fetchPolicy: "network-only"
  });
  return [data];
}

const Test = () => {
  const [data] = useGetPosts();
  return (
    <div>{data?.map(post => <h1>{post.text}</h1>)}</div>
  );
};

Building Out Three Reusable React Components

Now that we’ve seen some common signs of when to create a new component that you can share throughout your react application, let’s put that knowledge into practice and build out three practical demos.

1. Layout component

React is normally used for building complex web apps. This means that a large number of pages need to be developed in React, and I doubt that every page of an app will have a different layout. For instance, a web app consisting of 30 pages usually uses less than five different layouts. Therefore, building a flexible, reusable layout that can be utilized in many different pages is essential. This will save you very many lines of code and consequently a tremendous amount of time.

Consider the following React functional component:

// Feed.js
import React from "react";
import style from "./Feed.module.css";

export default function Feed() {
  return (
    <div className={style.FeedContainer}>
      <header className={style.FeedHeader}>Header</header>
      <main className={style.FeedMain}>
        {
          <div className={style.ItemList}>
            {itemData.map((item, idx) => (
              <div key={idx} className={style.Item}>
                {item}
              </div>
            ))}
          </div>
        }
      </main>
      <footer className={style.FeedFooter}>Footer</footer>
    </div>
  );
}

const itemData = [1, 2, 3, 4, 5];

This is a typical web page that has a <header>, a <main> and a <footer>. If there are 30 more web pages like this, you would easily get tired of repeatedly writing HTML tags and applying the same style over and over.

Instead, you can create a layout component that receives <header>, <main> and <footer> as props, as in the following code:

// Layout.js
import React from "react";
import style from "./Layout.module.css";
import PropTypes from "prop-types";

export default function Layout({ header, main, footer }) {
  return (
    <div className={style.Container}>
      <header className={style.Header}>{header}</header>
      <main className={style.Main}>{main}</main>
      <footer className={style.Footer}>{footer}</footer>
    </div>
  );
}

Layout.propTypes = {
  main: PropTypes.element.isRequired,
  header: PropTypes.element,
  footer: PropTypes.element
};

This component doesn’t require <header> and <footer>. So, you can use this same layout for pages regardless of whether they contain a header or a footer.

Using this layout component, you can turn your feed page into a much more sophisticated block of code:

// Feed.js
import React from "react";
import Layout from "./Layout";
import style from "./Feed.module.css";

export default function Feed() {
  return (
    <Layout
      header={<div className={style.FeedHeader}>Header</div>}
      main={
        <div className={style.ItemList}>
          {itemData.map((item, idx) => (
            <div key={idx} className={style.Item}>
              {item}
            </div>
          ))}
        </div>
      }
      footer={<div className={style.FeedFooter}>Footer</div>}
    />
  );
}

const itemData = [1, 2, 3, 4, 5];

Continue reading
A Practical Guide to Creating Reusable React Components
on SitePoint.

This article was republished from its original source.
Call Us: 1(800)730-2416

Pixeldust is a 20-year-old web development agency specializing in Drupal and WordPress and working with clients all over the country. With our best in class capabilities, we work with small businesses and fortune 500 companies alike. Give us a call at 1(800)730-2416 and let’s talk about your project.

FREE Drupal SEO Audit

Test your site below to see which issues need to be fixed. We will fix them and optimize your Drupal site 100% for Google and Bing. (Allow 30-60 seconds to gather data.)

Powered by

A Practical Guide to Creating Reusable React Components

On-Site Drupal SEO Master Setup

We make sure your site is 100% optimized (and stays that way) for the best SEO results.

With Pixeldust On-site (or On-page) SEO we make changes to your site’s structure and performance to make it easier for search engines to see and understand your site’s content. Search engines use algorithms to rank sites by degrees of relevance. Our on-site optimization ensures your site is configured to provide information in a way that meets Google and Bing standards for optimal indexing.

This service includes:

  • Pathauto install and configuration for SEO-friendly URLs.
  • Meta Tags install and configuration with dynamic tokens for meta titles and descriptions for all content types.
  • Install and fix all issues on the SEO checklist module.
  • Install and configure XML sitemap module and submit sitemaps.
  • Install and configure Google Analytics Module.
  • Install and configure Yoast.
  • Install and configure the Advanced Aggregation module to improve performance by minifying and merging CSS and JS.
  • Install and configure Schema.org Metatag.
  • Configure robots.txt.
  • Google Search Console setup snd configuration.
  • Find & Fix H1 tags.
  • Find and fix duplicate/missing meta descriptions.
  • Find and fix duplicate title tags.
  • Improve title, meta tags, and site descriptions.
  • Optimize images for better search engine optimization. Automate where possible.
  • Find and fix the missing alt and title tag for all images. Automate where possible.
  • The project takes 1 week to complete.