Chain React 2018: Simply React by Kent C. Dodds
One of the things I love about React is how easy it is to encapsulate UI code into a component. I love this because it makes reuse of those components easy. But is it simple? I've made my fair share of components that had to accept a bunch of props to handle a growing list of different use cases resulting in a complex render function full of conditional statements and a confusing API for people using this "reusable" component. Eventually it becomes too much and I had to make a new version of the same component to handle slightly different use cases. Lots of code/bug duplication there because my reusable component wasn't reusable enough. It wasn't simple enough.
There are patterns that have emerged in React that allow you to promote code reuse without sacrificing the simplicity or flexibility of the component. In this talk we’ll cover some of these patterns that will make your React components more useful.
Hi, everyone. My name is Kent C. Dods. I'm a software engineer at PayPal, and I'd like to invite everybody to please stand up. If you're physically able to join us, please. It's time to wake up. Hey. So you'll put your arms out in front of you like this. Please squat down and come back up. This is called exercise. We're going to do air squats with twelve of these together. And I want you to count out loud with me. Ready? One, two, three. Come on, count louder. Four, five. You're doing awesome. 6784. Wait, eleven and twelve. Okay, stretch as high as you possibly can, and then even higher than that. All right. And stretch over to one side and over the other. Awesome. Okay, before you sit down, introduce yourself to the person next to you and thank them for coming to the conference. Awesome. You can sit now. So this is so great. You're all just really enjoying talking. But I got to get to my talk. Just kidding. So I think that physical health is a really important part of just life and software development. We're more effective when we're physically healthy. So it's one of the reasons I like to do that also loosens me up, because no matter how many times I do this, I'm always nervous before I speak. So thanks for indulging me. I like to start my talks off with some expectations so that you know whether or not to get out your phone and check Twitter. So this talk, I'm going to go over the typical life cycle of a component, not the component lifecycle. You'll see what I mean in a little bit. We're going to look at how patterns can simplify our components and in what ways our components can get really messy and how our patents can simplify those. We're going to look at the composability of React, one of the reasons why we love React so much. And I'm going to give you a challenge. So prepare yourselves. I'm not going to be talking about how to implement these patterns. I'm going to be referencing a bunch of patterns that you may or may not have heard of before. Mostly this talk is to inspire you to learn about these patterns and then apply them to your components to make happier components and be happier using those components. So if you do want to learn about this stuff, definitely, like, learn React. I've got a free course about that. And then once you got React down solid, I've got another course on Egghead about these patterns specifically. So if you want to learn about the how for this stuff, then that's a good place to go. You ready to get started? Okay. Now that the kind of boring stuff is over. Okay, so we're going to be working with this autocomplete or this accordion component so I can open up the different items in the accordion. And it's pretty typical. And it allows me to it's not going to play again. Just remember that. Don't look away because you'll miss the video playback. But, yeah, so I can open up each one of these and you build this component. You're like, Sweet, I've solved my use case. I'm going to package that up, encapsulate that logic, make an accordion component, and then you just pass items to it that have a title and content. It's perfect, right? We've all built a component, or you've built components similar to these. And then somebody comes around to you and says, hey, listen, I saw that you have this accordion component. It's really awesome. And one of the things we love about React is how easy it is to share code between different places in the app, right? So could I maybe use that accordion component? And you say, yeah, totally. You just import accordion from my components, accordion. And then you just pass these items. They have to be structured like this. And they say, yeah, that's fine. I have one problem, though. My accordion needs to be rendered a little differently. I need it to render the content above the items. Okay, yeah, I can do that. So you get into your render method. You say, if above, like this, props above, then we'll render the content above. And then, yeah, that's fine. Not a big deal. And they say, Great, thanks. This is working so well. And then somebody comes over to that person and says, hey, that's a cool accordion. Where did you get that? And they said, oh, yeah, this person over here who built it, and they come to you and say, hey, I need to have an accordion in my part of the app, too, except I've got a slightly different use case. I need it to appear to the right. You're like, okay, yeah, that's just some Flexbox magic. And, yeah, I can make that work. And so you build that and you say, okay, now I have a right prompt. So this prompts right, then I'll do my Flexbox magic, and then somebody else comes and they say, hey, I need it to the left. And so now, okay, I got a left prop. And then you remember something that you heard on Twitter once and you think, oh, wait, I've got some impossible States. I could have a prop that's left is true and right is true. And now what do I do? So you refactor everything. Well, you don't refactor. You actually make a breaking change. And you say, okay, now I'm going to have a position. Prop position is left. Position is okay, now we're good. And the functional people will not hit me anymore. So that's good. So then somebody says, hey, you know that accordion that you built before they even talk, you say, don't ask me to change how it's rendered. Please don't ask me how they say, no, the original way it's rendered is totally fine. But I have a logic difference for my use case. So for mine, I can only open one at a time. So if somebody tries to open another one, it should close. You say, okay, yeah, that's just another prop single is true. And then in my handle accordion title click, I'll just do an if statement. Okay, yeah, that'll work. And now you've got a little bit more logic in your event handler there. And then somebody says, I also have a logic change. I need to prevent people from closing all of them so they can close some but not the last one. And you say, okay, yeah, I can do that. That's just another statement. But you're starting to feel a little bit uncomfortable about things. You're like, originally this thing was like 30 lines of code and now it's grown to like 300 lines of code. I'm not super excited about this. It's making me feel a little uncomfortable. And somebody says, hey, and we add the prevent close problem. Somebody says, hey, I want to do both of those things. So I can only have one open at a time and I can't close it. You say, oh, yeah, sweet. Thank you. I don't have to make any changes. You just have to provide both those props and then it'll work. Okay. But then this is when developers start to cry. Somebody comes and says, hey, that single prevent close accordion. That actually is exactly like a tabqi. Logically, it just looks a little different. So do you mind if I can make a pull request? But what if I built the render ability for a tabs UI and you're like, no, please. And they say, no, this will be so easy. It's just an if statement. How many times have you heard that? It's just an if statement, but in the render method, I'll just do if and you're crying. If this props tabs, then return this render tabs and I'll just have another method. It won't mess up your render method at all. Just be another render method. Okay. And you need to ship stuff, right? Like you've got customers. And so you say, okay, fine, yes. And then somebody comes and says, oh, I want to render the tabs below. Oh, shoot. And now you're sobbing and you eventually just create a new component, right? It's just like you build it. I don't need that. My use case. So this is what I mean when I say the lifecycle of a component. How many people have experienced something similar to this? Yes. Okay. I'm glad that I'm not alone. It's nice to have camaraderie there. So you don't have to actually see exactly what all these props are. But you get the idea what happens is these use cases start piling up and people start saying, hey, I want my accordion items to have a disabled so that I can disable an accordion. And then I want the open trigger to be on focus rather than on click or on press or whatever. I need to style things a little different. So can I have some class names, class name props, and you just start piling things up, like render, expand, all button. And yeah, that was the source of more tiers. So it's more than just the like, I don't like having all this complexity added to my component. There are actual technical problems with building components in this way. First off, for the web bundle size, it makes a big difference. And for native as well, performance can be a drain. So, like, you started with this 30 line component. It's really simple. Didn't really have much conditional logic. And it's growing and growing and growing to the point where you're like, maybe I should just write a one off because that thing is too complex for my use case. And so now you have a bunch of code in there that you don't actually need or use in your situation. Another problem is the maintenance overhead of having to think about so many different use cases is really a drain. It's not a lot of fun to deal with that. Yeah, that's no fun. I'm sure you know what I mean. And then as the complexity of your component grows, the possibility of bugs sneaking in increases. Also, I think there's like an exponential curve on that. And so that also makes me really sad. And the API, originally it was just like one prop items, but now we have as many props as you can imagine. When I was researching or looking for examples for this talk, I asked on Twitter, what's the most props that anybody has seen on a component? Somebody showed me an open source component that had 125 props. It was a reusable component. Yeah. I mean, it was a pretty complex date picker, but yeah, that just makes me cry twice. So somebody on that same thread said her name is Jen Creton. I call this an apocalypse. Yes, this is an apocalypse. I thought that was great. Let's make that a thing. Hashtag apocalypse. All right, advance. There we go. So all those examples that you saw before, what if I told you that they were all based on the same 37 line component using some of these patterns? And this is it. All right, I'll be honest, I did, like, scrunch up some of the lines so it would be shorter. So that's why this one's, like extra long prettier would have. But, you know, so, yeah, it's actually pretty relatively straightforward. We're using a couple of patterns in here that you may not be super familiar with. So that's why we have this internal set state and this changes object thing. And then we're using render props here. That's a big piece. Like all of the logic that we're adding. Most of that was going into our render method. Some of it was happening in our handle item. Click. But what we're doing here is it's called inversion of control. So we're saying, I don't want to make all the decisions. I'll come up with some good defaults, but I'm going to give you some control over how this stuff is managed. And that opens up a whole world of possibilities. That inversion of control means that you're giving up responsibility for certain things. But by giving up that responsibility and handing it off to the user, they can add the code that they need for their specific use case. So it's a little bit harder to use for simple use cases. But because of React's. Composability model, it actually is a really nice way to build components. So let's get a couple of examples here. You're going to need to watch because I can't play the video over again, apparently, which is unfortunate. So don't blink. Isn't that like from a movie or something? Okay, so here we have our standard accordion. And to change it to an above accordion. When that person comes and says, hey, I want an above accordion, you just say, you just take my standard accordion, copy, paste it, and you move stuff. You literally move the contents above the button and that's all you need to do. And they say, awesome, I can copy 20 lines of code, no problem, just move stuff. That's great. So that handles the render things above. What about the person who came and said, hey, let's render things to the left? Is that cool? And you say, hey, yeah, just take the person who had their above code, take that code and copy that and add a variance to the accordion item. And then you just change the emoji, right? And that accordion item will just have a little bit of logic in it to control what the CSS or the styles to apply to that element. Okay, cool. And then somebody says, hey, I want the right. I'm going from left to right. So take the left, and then just swap the content again, swap the content and the button. And now I have a really awesome right experience. So you're getting some code duplication here. And you could abstract some of this stuff into a single component to handle some of these use cases without making the base component any more complex. That's awesome. People opt into the complexity that they want. And then somebody says, hey, I want to take that standard component, but I want to add the single click behavior. So you say, great, I've implemented the state reducer pattern. And so now all you need to do is write a four line reducer function that will control that logic. And you just add that state reducer, that single reducer to the props and magic ensues it. Now behaves the way that you want it to. And then somebody says, well, that single reducer isn't doing what I want. I want to prevent clicks. And so you say, okay, yeah, just swap that single reducer for the prevent close reducer. And now you're set, and then that person comes and says, I want both, can I have both? And you say, yes. You write this ten line combined reducers function, and you can combine the prevent close and single reducers together to get that state behavior. Cool. And then the taps people come and they're like, hey, listen, I noticed that state reducer thing. Can I have my tabs? And you say, Absolutely, yes, you can. In fact, let's write a taps component right now. Here it is. You have your state reducer as a prop, and then all the other props, and then you combine the custom state reducer that anybody using the tabs component can also have their own reducer with the single and prevent close reducers. And then you just forward on the props. They can render it however they want, and it works great. So now anybody using the tabs component is actually using the same underlying logic from the accordion because lots of this stuff is exactly the same. And then they get the benefit of your state reducer. So cool. Let's see what that looks like in practice. Somebody built their standard tabs, and then they want to add swap it from below to above. Then you just take that standard tabs accordion or that standard tabs and swap the items and the buttons and magic ensues. And through the whole course of all this stuff, nobody needed to come and ask you questions about how to use your component or change your component. We didn't have to change your component at all. And we continue working on other really fun components, like Date Pickers that don't have 125 props. So this is great. I'm really excited by these patterns. How many people here have used downshift or heard of downshift? I said used or heard of, because I'm going to assume you've all used it so great. So downshift is an autocomplete component where I learned about and implemented all these patterns. It works in react, native, really super. But I just want to give you a word of caution about these patterns. One question people often ask me about the react component patterns that I'm always talking about is, are these just for highly reusable components? And the answer is, it's more obvious when to use this for highly reusable components. But that's not just what these are useful for. This is also useful for separating concerns. You've got your logic components, and then you've got your presentation components really easy to separate those with some of these patterns. And being able to do that can really enhance the productivity of engineers working on your code base. So here's the word of caution. What if this was all you ever needed, like in the first place? You build out this really highly complex component and then this simple one on top of it to do all the rendering. And if this is all you ever needed, then you probably wasted a bunch of time and added some complexity. Every abstraction comes with complexity. You cannot get around that. A function comes with the cost and so you need to determine whether the benefits outweigh the cost. As much as I love these patterns, I don't use them in every component that I built. I will definitely build one off components that just that original accordion. It's 30 lines long, it's very simple and I'm happy with that. And then somebody comes with a different use case and I'll add an if statement. There's nothing wrong with an if statement, although I kind of prefer ternaries, but whatever. So feel free to add a couple of use cases only when you notice that hey, this could actually be made a lot simpler if we change this to use some other patterns that you can add that complexity and feel a little bit better about the cost and benefits. So the whole point of this talk is I want to encourage you to learn about and use patterns that simplify your API the API of your component as well as the implementation. So some of the patterns that we looked at and some of the patterns I'm excited about render props state reducers compound components. We didn't look at those, but those accordions are pretty good use case for compound component components. Control props really powerful. The provider pattern and there are a bunch more and one of the cool things that I like about software and one of the interesting things is that we're always rediscovering things that they knew like three decades ago but we're applying it to what we have today and I think that's pretty cool. So let's go do some research, play around with things and reduce discover some more patterns that we can apply to our components to make our component APIs and implementation simpler. That's all I have for you. Thank you very much.