This video is available to students only

How to Build a React Higher-Order Component

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

Previous LessonAn Intro to React Higher-Order Components or HOCsNext LessonPass Refs Through Higher-Order Components With forwardRef

Lesson Transcript

  • [00:00 - 00:08] Instrument adapter has a higher order component. Higher order components are like higher order functions, but in the realm of React components.

  • [00:09 - 00:20] How it is described in the React documentation, conceptually components are like JavaScript functions. They accept arbitrary inputs, called props, and return React elements describing what should appear on the screen.

  • [00:21 - 00:30] So we can say that a component is a function of some data passed via props. Therefore, we can continue this analogy with functions and extend it.

  • [00:31 - 00:40] What would a higher order component be? While a component transforms props into UI, a higher order component transforms a component into another one, and hence somehow.

  • [00:41 - 00:54] In our case, the enhancement would be connecting a component to a sound font functionality. Inside of the SRC adapters, create new file with instrument, TSX, and add the following code.

  • [00:55 - 01:02] Here we're exporting the component and component type from React. We'll need component to extend it and define our class-based hook.

  • [01:03 - 01:15] We import sound font, instrument name, and player, from sound font player, MIDI value from the node domain, optional from types, audio notes registry, and default instrument from sound. Sort of like we did for the use sound font.

  • [01:16 - 01:22] The public API for adapter will stay the same as it was before. However, provided props would be called injected props.

  • [01:23 - 01:30] Since we would inject them into a component that we will enhance. Define new type injected props.

  • [01:31 - 01:41] It's an object with field loading, boolean play. It's going to play notes of type media value, and return promise of type void.

  • [01:42 - 01:47] Same with the stop. Then we define provider props, type provider props.

  • [01:48 - 02:01] It's an object with audio context, of type audio context, type, and instrument of type instrument name. Then define the provider state, type provider state.

  • [02:02 - 02:05] It's going to contain the loading. We put it into state because we need it to be observable.

  • [02:06 - 02:13] Type is boolean and the current instrument. Current optional instrument name.

  • [02:14 - 02:20] All right. Now let's define the factory function, expert function with instrument.

  • [02:21 - 02:28] We provide the props type here, and we'll define a type argument. The props, we will make it extend.

  • [02:29 - 02:33] Injected props. The extends keyword here is called generic constraint.

  • [02:34 - 02:45] We use it to define tprops so that it must include properties described in the injected props. And if we provide something else that doesn't match injected props, then TypeScript should give us an error.

  • [02:46 - 02:55] Then we provide the default type here, injected props, and then we define the properties in the argument. Wrapped component is going to be a component.

  • [02:56 - 02:59] Type that will receive tprops. All right.

  • [03:00 - 03:04] Form of the document. Now inside, let's define the display name.

  • [03:05 - 03:14] Const display name equals wrapped component display name. Or wrapped component name.

  • [03:15 - 03:22] Or in worst case, if we have neither, then it's just a component. This field display name is useful for debugging.

  • [03:23 - 03:31] A container component that we're going to create will show up in developer tools like any other component. So it better give it a name to make it recognizable in the inspector.

  • [03:32 - 03:40] Next, below the display name, we define the class that we're going to return. This is going to be the container component that will enhance our wrapped component.

  • [03:41 - 03:54] Return class with instrument extends component. We pass in provider props as the prop type and the provider state type as the state type.

  • [03:55 - 04:00] Define the properties. They're the same as in the sound font provider class from their Andrew props example.

  • [04:01 - 04:06] We can even go there and copy them. Copy, go to with instrument and provide them here.

  • [04:07 - 04:15] Let's form of the document. If you look at sound font provider class again, you will see that we can actually even copy the constructor and all the other methods.

  • [04:16 - 04:24] Constructor component, mount component, did update and shoot component update. Let's copy lifecycle events and the constructor.

  • [04:25 - 04:27] Paste it here. Form of the document.

  • [04:28 - 04:32] Now let's define the load function. Take the load and paste it here.

  • [04:33 - 04:40] Now let's copy the resume, play and stop functions. Copy the resume, paste it in the hook.

  • [04:41 - 04:47] Then do the same with the play and stop methods. Put them below the lifecycle events.

  • [04:48 - 04:52] Let's format our document. And now we need to define the render method.

  • [04:53 - 05:01] This is where our component is going to be different. Instead of using the render prop like we did in the render prop example, we're going to render the wrapped component.

  • [05:02 - 05:05] Public render. Here we define the injected props.

  • [05:06 - 05:16] Injected equals an object loading equals this state. Loading play this play and stop this stop.

  • [05:17 - 05:31] We cast it to injected props as injected props. And now we return wrapped component where we pass all the injected props injected and we cast it to T props.

  • [05:32 - 05:44] We have to cast the injected props to T props here because there is an issue in TypeScript that erases type of props when using this pred operator. This forces us to explicitly cast injected props into T props type.

  • [05:45 - 05:54] The hooks that inject new props to a given component are called injectors. They're useful when we have cross-cutting concerns in our app and we don't want to implement the same functionality repeatedly.

  • [05:55 - 06:11] For example, now we can use our with instrument hook with not only keyboard, but with any component that expects play, stop and loading props to play notes. We can create a trombone component or guitar component and it will still work with this hook.

  • [06:12 - 06:18] Now let's go to index.ts and re-expert everything from the with instrument.ts. Using hook with keyboard.

  • [06:19 - 06:43] Inside of the keyboard folder, create a new file, keyboard with instrument.tsx, import the with instrument hook with instrument from adapters, sound font, define the wrapped keyboard, const wrapped keyboard equals with instrument and here we pass the keyboard component. That's going to be enhanced.

  • [06:44 - 07:01] Impert the keyboard from keyboard module and now expert const keyboard with instrument is going to be a function that will get audio context. Using the use audio context hook, we import it from the audio context provider.

  • [07:02 - 07:15] We'll define the instrument. It comes from use instrument and then we return wrapped keyboard to which we'll pass the audio context, audio context and the instrument.

  • [07:16 - 07:20] Here you go. So here you can see how with instrument hook is being used.

  • [07:21 - 07:30] It takes keyboard component that requires loading planes top as props and returns the wrapped keyboard. That requires audio context and optional instrument props.

  • [07:31 - 07:42] This is possible because a keyboard becomes wrapped component when we call with instrument. Basically wrapped keyboard is a with instrument class that renders out a keyboard with remembered injected props.

  • [07:43 - 07:57] If you open our app in the browser now, you will see that our keyboard is wrapped into keyboard with instrument using the with instrument hook. You can also see which props does it receive and what does it store in the estate.

  • [07:58 - 08:03] Congratulations you've just rewrote the adapter to a higher order component pattern.

This lesson preview is part of the Fullstack React with TypeScript Masterclass course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

Unlock This Course

Get unlimited access to Fullstack React with TypeScript Masterclass, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course Fullstack React with TypeScript Masterclass