Advanced React Patterns Advanced React Patterns

Render Prop Component Solution

Check out a free preview of the full Advanced React Patterns course:
The "Render Prop Component Solution" Lesson is part of the full, Advanced React Patterns course featured in this preview video. Here's what you'd learn in this lesson:

Kent separates the component into a render property component and a view component that accepts renderable properties. Kent iterates through the steps to create the separation.

Get Unlimited Access Now

Transcript from the "Render Prop Component Solution" Lesson

[00:00:00]
>> Kent C. Dodds: Yeah, let's go ahead and look at this problem and solution for making render prop components. So I'll run the tests with npm run test. And swap the usage to the exercise version. We get our error. It's unable to find a label with the text of custom button.

[00:00:22] And that's because despite the fact that we are rendering a button with that label, it can't find it because our render is actually just returning the switch component. So let's work through this step by step so we can really get an understanding of how the render prop pattern works.

[00:00:45] So I'm actually gonna go ahead and remove this just to, oops, get rid of some additional stuff. So the render prop pattern is, I've found to be surprisingly simple. And it's a lot simpler than you would think of it first. So what I'm gonna do to illustrate this though, is I'm gonna rename this render function.

[00:01:08] I'm gonna call it render switch. And then I'm gonna make an actual render function. And this is going to return this.renderSwitch. Okay, regular JavaScript stuff, nothing fancy going on here. You might be looking at that and like, okay, that was pointless. But hold on, stick with me. So let's go ahead and, instead of using this inside of our render switch function, let's use attributes.

[00:01:35] So we'll pass in options. And make this function a pure function that accepts arguments, returns JSX. Returns React elements. So we're gonna need the on and the toggle. So we'll just get rid of that, and we'll get rid of that. Okay, cool. So now things are still, actually.

[00:01:56] And we'll need to provide those, so, on is this.state.on. And toggle is this.toggle. And now things are equally broken, so so good so far [LAUGH]. If we look at the finished version, though, it still functions the way that it did before. So the next thing that is kind of interesting is because this is now a pure function, it doesn't use the instance inside.

[00:02:24] You don't see this inside of here. When a method on a class doesn't use this, then it doesn't need to be on the class. So you can make it a static property. So now I can say. And actually, we'll have to make it an assignment to an arrow function.

[00:02:45] There we go. And then we can say, toggle.renderSwitch. Okay, it's still just as broken as it was before, but it still actually functions there. Okay, so does it really need to be on the toggle class? Well, no, we can actually make this a const and move it out.

[00:03:07] And then just use render switch. Okay, so we're incrementally getting closer and closer to what we're supposed to be doing here. It's a pure function, so it doesn't need to be a part of the toggle instance or even a part of the toggle class. It's just an extra function that is responsible for the rendering of our component.

[00:03:27] And all we have to do is pass in some whatever that function needs to do its rendering so that it can be a pure function. So yeah, still equally functional and broken at the same time, ha. Okay, great. So what if instead of referencing renderSwitch inside of the scope of this module, what if we accepted it as a prop instead?

[00:03:52] this.props.renderSwitch. And then in our usage, we'll just pretend this isn't here for a second. We'll return Toggle onToggle. And then the renderSwitch prop, what should we assign that to? Well, let's just continue with our iterating on this. We'll assign it to the same thing we had before. So it should be functionally equivalent to what we had before.

[00:04:25] We have not changed any of the functionality from the very start of this component. It functions exactly the same as it did before. Okay, well, this is a prop. And children is actually a prop. And so we could change this to childrren. And then if you recall earlier, somebody asked a question about the React.chilrdren.map and why would you use that over array.map.

[00:04:52] Well, that's because the children prop will either be an array of all the items that you pass it or it will just be the one item that you've passed. And so in this case, we're actually explicitly passing a single item, so we can call it directly, okay. So we're just passing render switch.

[00:05:11] And that will still function as it did before. And actually, what's interesting is that this is now using render props. So the rendering responsibility is now under the ownership of the user and not the component implementation. And so at this point I can refactor this. Because children is the implicit prop between the opening and closing of your JSX.

[00:05:44]
>> Kent C. Dodds: That is functionally equivalent to what we just had.
>> Kent C. Dodds: Okay, cool. So often with render props, you're gonna see, you're not gonna actually declare the function outside, you'll have it declared inline. So we could just take this, cut it out and stick it right there. Okay, so it's still broken.

[00:06:07] But let's just, really quick, switch to our other implementation. And now it works.
>> Kent C. Dodds: I'm seeing some head nods, so that's good. So we didn't actually do a whole lot. Like the whole purpose of this is to say, hey, I'll be in charge of your state. You be in charge of rendering.

[00:06:36] And what's really cool about this, and when I take a step back and think, okay, how does this pattern compare to other things? If I can create another component that's built on top of this one that exposes the same API as the original, that's when I know I've got a really good pattern.

[00:06:54] And so I could say [INAUDIBLE], or we'll just do a function, OriginalToggle. And this'll take the same defaults here. And we'll just return. Actually here we don't need the defaults, we'll just say props. And we'll return Toggle. And inside of here we'll take our on and toggle. Destructure those off.

[00:07:25] And then render this switch. Oops, not a switch expression. With on is on and on click is toggle.
>> Kent C. Dodds: And these props, we'll just spread across here. And then I can actually use. I'll switch this usage here, return toggle.
>> Kent C. Dodds: And we have the same original implementation. So if you don't like the verbosity of a render prop, you just make another component that uses that render prop, and then you can use the nice, cleaner, smaller API that you're looking for.

[00:08:14] Without sacrificing the flexibility that render props provide.
>> Kent C. Dodds: And this is why I love render props. Because they serve as a really good foundation for a component that wants to share, to control its logic, and allow total flexibility of the UI.