Sometimes you have some boilerplate for components that would be nice to abstract away slightly with a simple helper function. In this lesson we'll learn how to create a "Higher Order Component" which is a component factory function. We'll use it to give a given component a prop for our context value.
Instructor: [00:00] The first thing we're going to do is make a function called with toggle that's going to accept a component. Here we're going to make a wrapper and that'll accept props and return the toggle consumer.
[00:12] The toggle consumer's going to give us the toggle context. We'll render that component with a toggle prop, and we'll pass the toggle context.
[00:21] Then we'll forward along all the props. Then we'll return our wrapper.
[00:28] This is the simplest form of a higher-order component that we could implement for our toggle. Let's go ahead and use that for our layer 2 and layer 4 which are both using using the toggle consumer. We'll say with toggle, and that's going to accept our arrow function which our component.
[00:44] Now this arrow function is going to accept a prop that is our toggle context. We'll destructure the toggle from props, then we'll destructure out from that the on property.
[00:55] Then we can get rid of the toggle consumer and its arrow function child. On is now coming from this wrapper which is rendering our function component with the toggle prop.
[01:06] Let's do the same thing for layer 4. We'll say with toggle. We'll destructure toggle and get on, and the toggle call-back.
[01:15] Then we can swap out everything with just that switch. Everything is working great.
[01:22] There are a few things you need to consider when you're making a higher order component. One of them is, you want to make the fact that this layer 2 is actually the wrapper as unobservable as possible. You want your wrapper to resemble the component that is being passed as much as you can.
[01:39] If that component has some static properties on it, we want the wrapper to have all of those same static properties. Luckily for us there's a module for this. It's called hoist-non-react-statics. Here we can simply return hoist-non-react-statics of the wrapper and the component.
[01:57] We'll put all of the same static properties that the component has onto the wrapper so that users of layer 2 can assume that they're interacting with the component that it's rendering. This will make the usability of higher order components a lot better.
[02:11] The next thing we need to be concerned about is if someone wanted to access the instance of this component with a ref. For example, if somebody creates a myref with react.createRef, then they pass that to the ref prop of our layer 2.
[02:28] Right now that actually won't work because the wrapper is what layer 2 is, and a wrapper is a function component which doesn't have an instance. In any case, the wrapper is probably not what people are going after when they try to render layer 2 with a ref.
[02:42] Let's go ahead and continue to make the wrapper resemble the component that it is wrapping a little further by forwarding any refs that it receives. We're going to use react.forwardref to the wrapper.
[02:57] The way that works is our wrapper is now going to accept props and a ref. Then we can say ref equals ref, and react forward ref will handle that forwarding for us.
[03:07] The last thing that we need to consider is the display name. Here we have our layer 1, and then we have a forward ref for wrapper, and then a context consumer, and then another unknown.
[03:20] These display names here are not very useful. Let's go ahead and make the display name of our component a little nicer.
[03:26] We'll simply say wrapper.displayname equals with toggle. We're wrapping the component and we'll use the display name if it exists. Otherwise, we'll use the component.name.
[03:43] With that, now our forward ref display name is a little bit better, but we're still missing something here. What's going on here is the component doesn't actually have a name, because it's an anonymous arrow function.
[03:56] We could solve this a couple of ways. We could make this a named function, so we'll call it layer 2. Then it couldn't be an arrow function anymore. No one puts a return, so we'll explicitly return and we'll add our closing bracket.
[04:11] Now that's a little bit more helpful. That's a complete higher-order component.
[04:15] In review, a higher-order component is a function which returns a component. It can accept a component. It could also accept options or any number of other arguments. All that it needs to be is a function that returns a component.
[04:27] In our case, our wrapper component accepts props and a ref from react forward ref, and then renders our toggle consumer which receives the toggle context and forwards that along to the underlying component that it's wrapping. It also handles refs properly with that ref from forward ref, and it specifies a display name so the dev tools look nicer.
[04:49] Then we also take all of the static properties on the component and put them directly on our wrapper, so that users of the wrapper from the with toggle higher order component can assume that the layer 2 resembles very closely the component that it is wrapping.
Tomeu, this is actually one of the reasons that higher order components have fallen out of favor in the community. Generally prefer a render prop over a higher order component :)
Anyone has tried this with Typescript? It's quite hard to define/type the props and the result component :/