So in that way, you are essentially doing like functional composition, but with React components. And what you can do is pass them things via props because components get things from the outside by props, or at least they did in the old world. That was the way that they got them. So that's where higher-order components came from.
Obviously, you've probably seen quite a few high-order components, like Connect being a very famous one with Redux, like, that also like with Router from React, Router is another very well-known high-order component as well from the libraries. So it was very…all of this sort of React ecosystem very much like adopted that pattern as well, like as a way to share stateful logic or component logic, maybe is a better way of saying it.
And then, yeah, going forward in time a bit further, we got this idea of the RenderProps pattern which allowed us to make our composition a bit more declarative and essentially do our composition at render time. So the way that that works, just following on from the higher-order components is you actually only need some sort of dependencies, like when a component actually renders. And in React, you've got this composition model where you can pass down children. We'll see code examples of this later, so don't worry if you're thinking this sounds a bit abstract.
But you can, you know, you can have a function as a child. So, as you can see on this graphic on the left, it's like you've got some React component and you say, okay, my children are a function, that function can be invoked from whoever this parent is with some arguments, and then in the parameters here, you can pull off whatever it is you need and just return some more components. And you sort of got, like, the things that you need at render time, which is cool. And so, yeah, this was something that really gained a lot of momentum. Again, it's a pattern that's still around, and it's also a pattern that's still used quite a lot. It's really relevant. That's why I'm sort of talking about all of these things. This one is still very much, you know, on a day-to-day, you'll see it around. Hire all the components less so that we'll have a look at why that is in the examples too.
And then, finally, this latest evolution, which I'm sure you're all aware of because it's been, I think, a couple of years now, the hooks came about. And what React Hooks allowed us to do was essentially do this composition perpendicular to the tree. I really like this way of thinking about it, because it really helps actually, by the way, always to think of your components, your whole application really as a tree structure. And in that way, you can reason about, like, who's the parent, who are the children. And, you know, change of state or props and apparent is what triggers the children to re-render. So it's always worth thinking in this kind of way, like in the tree, like what is the hierarchy? But yeah, essentially hooks, what it does is instead of having to do it vertically, which both the other patterns do, you can do it horizontally, because you can just write a piece of component logic and then you can just hook into that piece of component logic from any component anywhere in the tree. That's sort of the big idea. So yeah, hopefully that makes sense, but we will see some more.
So if we are using functional components and we want to, yeah, like reuse some sort of, in this case, a side effect, useDocumentTitle, then we can quite simply just use it. So you see on the left-hand side, function here, calling it useDocumentTitle. So it's a hook, takes in a title, and then it just runs this effect, right? So, the usedEffect hook, everything that's a side effect should use that for, and in this case, obviously, using this document API is a side effect. So just run that and then, yeah, inside any given component, you would just import useDocumentTitle and pass it that string which is the title it's expecting. Simple as that.
But then, yeah, you're probably thinking, what if I hook returns something? Well, yes, good question. So, if the hook returns something, so just building on that same example here, you can see at the top here a custom hook which takes in the title as an argument, and it also returns an update function and the current count. So, just adding a little bit of state into the equation here, like inside this custom hook. And yeah, what it's going to do in that useEffect is just run the effect there and then print out the count and title, so we would see that in the top of the document. And from the hook, there we're returning an object with two different things, like the current count itself, and also the update function which is the set counter, which we defined up at the top of the useState. And then, yeah, if we wanted to use that custom hook in a functional component, very simple, yeah, there might be some functional component called document.titleEffectFnComponent, and all we need to do is just use our ES6 deconstructing here to define two variables, count and setCount, because we know that's what's returned from that object, and then pass in our argument as well. So in this case, we've also passed in an object with like title, hey. Yeah, hopefully that all makes sense. This is pretty sort of like one-on-one stuff at the moment for hooks, but we're building up. So, cool, so, yeah, I guess, leading on from this, what do you do, though, when you've got like a nice, custom hook, like the one we just saw, and you're like, oh, that's so cool, I can change my documentite. I'm not sure if you'd want to, but you might. But all your components are class components. You're like in this world where it's a legacy code base. Yeah, firstly you might say, well, I could refactor everything to hooks, but that might not be viable for various reasons, right? Because your components might be really large and hard to refactor. There might be a lot of complex logic. It also might be sort of really well tested, and you don't have time to rewrite the tests. It might simply come down to the fact as well that this thing about time is really interesting because it's like, what's the relative benefit of the refactor? And that's one of the things that we have to judge because we have to sort of, I guess, weigh that against delivery. Like if you need to deliver new features, then you need to deliver new features. And obviously you can write anything new using sort of the new syntax and everything, assuming that you've updated your version of React, which you should because it has great backwards compatibility but yeah. I mean, you might have this situation. Certainly where I work, being a 160 year old company, nothing about legacy code and like, yeah, like sometimes there's gonna be like co-based as well. That just hasn't been possible in maybe part of the co-based and maybe part of it started to move. So that's what we're gonna get into now. So what kind of happens, I guess, is the first question. Like when we try to use hooks in the class component, maybe some of you have tried, but I will tell you, you basically get an error saying, can only be called in a functional component. Error message looks like that. So yeah, it does tell you what the problem is. This is obviously sort of one of the rules of hooks. I do recommend, you know, like working with hooks, just generally like, definitely read those because it makes all of the errors that you can't make a lot more sense. There are actually that sort of links in the slides. The slides will be shared at the end, so you can click on any of the links directly. I'll also post them all in the Discord channel. I'll open up some of them, but probably not this one as just the React documentation. So yeah, when we're faced with this situation, we need a way forward that balances codary factors with delivery. That's normally the reason why this sort of thing comes around. So yeah, what we could do then in order to start solving this is start by following the error. So some logical side effects in our custom hook that we wanna reuse. We'll start with that.
Comments