Reader small image

You're reading from  Learn React with TypeScript - Second Edition

Product typeBook
Published inMar 2023
Reading LevelBeginner
PublisherPackt
ISBN-139781804614204
Edition2nd Edition
Languages
Tools
Right arrow
Author (1)
Carl Rippon
Carl Rippon
author image
Carl Rippon

Carl Rippon has been in the software industry for over 20 years developing a complex lines of business applications in various sectors. He has spent the last 8 years building single-page applications using a wide range of JavaScript technologies including Angular, ReactJS, and TypeScript. Carl has also written over 100 blog posts on various technologies.
Read more about Carl Rippon

Right arrow

Reusable Components

In this chapter, we will build a checklist component and use various patterns to make it highly reusable but still strongly typed.

We will start by using TypeScript generics to strongly type the data passed to the component. Then, we will use the props spreading pattern to make the component API-flexible, and allow consumers of the component to custom render parts of the component using the render props pattern. After that, we will learn how to make custom hooks and use this to extract logic for checked items and how to make the state within a component controllable to change the component’s behavior.

We’ll cover the following topics:

  • Creating the project
  • Using generic props
  • Using props spreading
  • Using render props
  • Adding checked functionality
  • Creating custom hooks
  • Allowing the internal state to be controlled

Technical requirements

We will use the following technologies in this chapter:

All the code snippets in this chapter can be found online at https://github.com/PacktPublishing/Learn-React-with-TypeScript-2nd-Edition/tree/main/Chapter11.

Creating the project

In this section, we will create the project for the app we will build and its folder structure. The folder structure will be straightforward because it contains a single page with the checklist component we will build.

We will develop the app using Visual Studio Code as in previous chapters, so open Visual Studio Code and carry out the following steps:

  1. Create the project using Create React App. See Chapter 3, Setting up React and TypeScript, if you can’t remember the steps for this.
  2. We will style the app with Tailwind CSS, so install this into the project and configure it. See Chapter 5, Approaches to Styling Frontends, if you can’t remember the steps for this.

That completes the project setup.

Using generic props

In this section, we’ll take some time to understand how to create our own generic types and also learn about the keyof TypeScript feature, which is useful for generic types. We will use this knowledge to build the first iteration of the checklist component with a generic type for its props.

Understanding generics

We have used generics throughout this book. For example, the useState hook has an optional generic parameter for the type of state variable:

const [visible, setVisible] = useState<boolean>()

Generic parameters in a function allow that function to be reusable with different types and be strongly typed. The following function returns the first element in an array, or null if the array is empty. However, the function only works with a string array:

function first(array: Array<string>): string | null {
  return array.length === 0 ? null : array[0];
}

Generics allows us to make this function usable with any type...

Using props spreading

In this section, we’ll learn about a pattern called props spreading. This pattern is useful when you want to use all the props from an HTML element in the implementation of a component. In our checklist component, we will use this pattern to add all the props for the ul element. This will allow consumers of the component to specify props, such as the height and width of the checklist.

So, carry out the following steps:

  1. Open Checklist.tsx and import the following type from React:
    import { ComponentPropsWithoutRef } from 'react';

This type allows us to reference the props of an HTML element such as ul. It is a generic type that takes the HTML element name as a generic parameter.

  1. Add the props from the ul element to the component props type as follows:
    type Props<Data> = {
      data: Data[];
      id: keyof Data;
      primary: keyof Data;
      secondary: keyof Data;
    } & ComponentPropsWithoutRef...

Using render props

In this section, we will learn about the render props pattern and use it to allow the consumer of the component to render items within the checklist component.

Understanding the render props pattern

A way of making a component highly reusable is to allow the consumer to render internal elements within it. The children prop on a button element is an example of this because it allows us to specify any button content we like:

<button>We can specify any content here</button>

The render props pattern allows us to use a prop other than children to provide this capability. This is useful when the children prop is already used for something else, as in the following example:

<Modal heading={<h3>Enter Details</h3>}>
  Some content
</Modal>

Here, heading is a render prop in a Modal component.

Render props are useful when allowing the consumer to render elements associated with the data passed into the component...

Adding checked functionality

Currently, our checklist component doesn’t contain the ability to check items, so we will now add checkboxes to the list of items, giving users the ability to check them. We will track the checked items using a React state.

So, carry out the following steps to add this functionality to our component:

  1. Open Checklist.tsx and add useState to the React import statement:
    import {
      ComponentPropsWithoutRef,
      ReactNode,
      useState
    } from 'react';

We will use the state to store the IDs of the checked items.

  1. At the top of the component implementation, add the state for the IDs of the checked items:
    const [checkedIds, setCheckedIds] = useState<IdValue[]>([]);

We have referenced an IdValue type that we haven’t defined yet – we’ll define this after we have finished the component implementation in step 6.

  1. Add checkboxes to the list of items as follows:
    <li...

Creating custom hooks

In this section, we’ll learn about custom React hooks. Then, we will use this knowledge to extract the checked logic from the checklist component into a reusable custom hook.

Understanding custom hooks

As well as standard hooks such as useState, React allows us to create our own custom hooks. Custom hooks allow logic to be isolated and reused across multiple components.

A custom hook is defined using a function with a name that starts with the word use. This naming convention helps ESLint check for problems with the use of the custom hook. Here’s a custom hook that provides toggling logic:

export function useToggle() {
  const [toggleValue, setToggleValue] = useState(false);
  function toggle() {
    setToggleValue(!toggleValue);
  }
  return {toggleValue, toggle};
};

The custom hook contains the state of the current toggle value, which is either true or false. It also includes...

Allowing the internal state to be controlled

In this section, we’ll learn how to allow consumers of a component to control its internal state. We will use this pattern in the checklist component so that users can check just a single item.

Understanding how the internal state can be controlled

Allowing consumers of a component to control the state allows the behavior of a component to be tweaked if that behavior is driven by the state. Let’s go through an example using the useToggle custom hook we covered in the last section when learning about custom hooks.

Two additional props are required to allow the internal state to be controlled – one for the current state value and one for a change handler. These additional props are toggleValue and onToggleValueChange in useToggle:

type Params = {
  defaultToggleValue?: boolean;
  toggleValue?: boolean;
  onToggleValueChange?: (toggleValue: boolean) => void;
};
export function...

Summary

In this chapter, we created a reusable checklist component and used many useful patterns along the way.

We started by learning how to implement generic props, which allow a component to be used with varying data types but still be strongly typed. We used this to allow varying data to be passed into the checklist component without sacrificing type safety.

We learned how to allow consumers of a component to spread props onto an internal element. A common use case is spreading props onto the internal container element to allow the consumer to size it, which is what we did with the checklist component.

The render prop pattern is one of the most useful patterns when developing reusable components. We learned that it allows the consumer to take responsibility for rendering parts of the component. We used this pattern to override the rendering of list items in our checklist component.

Custom hooks isolate logic and are useful for sharing logic across components and keeping...

Questions

Answer the following questions to check what you have learned in this chapter:

  1. The snippet of the following component renders options and one can be selected:
    type Props<TOption> = {
      options: TOption[];
      value: string;
      label: string;
    };
    export function Select({
      options,
      value,
      label,
    }: Props<TOption>) {
      return ...
    }

The following TypeScript error is raised on the component props parameter though: Cannot find name ‘TOption’. What is the problem?

  1. The value and label props from the component in question 1 should only be set to a property name in the options value. What type can we give value and label so that TypeScript includes them in its type checking?
  2. A prop called option has been added to the Select component from the previous question as follows:
    type Props<TOption> = {
      ...,
      option: ReactNode;
    };
    export function Select<...

Answers

  1. The generic type must be defined in the component function as well as the prop:
    export function Select<TOption>({
      options,
      value,
      label,
    }: Props<TOption>) {
      return ...
    }
  2. The keyof operator can be used to ensure value and label are keys in options:
    type Props<TOption> = {
      options: TOption[];
      value: keyof TOption;
      label: keyof TOption;
    };
  3. The consumer is likely to need the data for the option, so the prop should be a function containing the data as a parameter:
    type Props<TOption> = {
      ...,
      renderOption: (option: TOption) => ReactNode;
    };
    export function Select<TOption>({
      options,
      value,
      label,
      renderOption,
    }: Props<TOption>) {
      return (
        <div>
          <input />
          {options.map((option) =>...
lock icon
The rest of the chapter is locked
You have been reading a chapter from
Learn React with TypeScript - Second Edition
Published in: Mar 2023Publisher: PacktISBN-13: 9781804614204
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime

Author (1)

author image
Carl Rippon

Carl Rippon has been in the software industry for over 20 years developing a complex lines of business applications in various sectors. He has spent the last 8 years building single-page applications using a wide range of JavaScript technologies including Angular, ReactJS, and TypeScript. Carl has also written over 100 blog posts on various technologies.
Read more about Carl Rippon