In this video, I want to show you a mistake that I see a lot of developers make in their React applications that's not so obvious, because this mistake doesn't really affect junior developers all that much, but it mostly affects developers that have some experience with React, they understand how to use React and kind of the mental model of React, but then they still fall into this mistake because it's very easy to fall to, because it comes from good intentions. And this is actually a mistake that you're probably not going to find if you do a search on Google or YouTube on like top React mistakes, because people don't talk about this one. Again, it's a little bit more nuanced, it's not so black and white, but it's extremely important because it really makes the difference between a mid developer and a senior developer, in my opinion.
So if you're down to explore this mistake with me, then sit back, relax and enjoy the show, and also subscribe. It's completely free, and I plan to make a bunch more videos just like this one. Alright, cool.
So let's begin. So over here, as usual, I have an application that I'm going to use to essentially demo you this mistake, and everything is going to happen in the app component. And then in the user card component that we have here at the top.
Now, the mistake over here is, like I said, in the introduction is a mistake that actually comes from good intentions. It's come, it comes from the intentions of wanting to use React the way that it's supposed to be used, right. So when you think about React, right, React is a framework or a library, whatever you want to call it, that is designed to be made up of multiple components, multiple small pieces, and you put those components together.
And more importantly, you should as a developer, try to find ways to reuse components in multiple places, instead of duplicating code, and writing the same thing over and over again in all these different places. And while that's generally a very good thing to do, and it's something that I try to teach in all of my videos, and I recommend that all of you here watching actually learn this, there comes a point where you can do that too much, and it actually becomes detrimental to the actual project, it becomes overcomplicated, over engineered. And it's not the way that I would recommend that you build your application.
So with this, with this example, I want to actually show you one of such cases and show you why it's bad. And then we're going to spend a little bit of time just fixing it to see how this could be better. And actually, that's very important.
As we go through this code, I want you to keep in mind that this is not bad code, I'm not here telling you that this is bad code versus good code, it's not, this is actually good code. I'm here to tell you the difference between good code and better code. So with that being said, now, let's actually look at the code.
So what we have here in this component is we have this object over here, it is called employee data, and it has a bunch of properties that are related to an employee. And then what we have here right below is we have another object that says customer data that also has a bunch of properties that are related to a customer. Now, both of these objects here, employee data and customer data actually will share some properties like their ID over here, it's the same in both objects.
But they will also have some properties that are different. For example, an employee is going to have years of service and project count. And then a customer is going to have total purchases and then last purchase date.
So some things are similar and other things are different. And so now if I asked you to use these objects over here to use this data and actually build me and show this data in the actual UI, how would you do it? How would you create your components?
Like how would that look like? How would the structure look like? Well, let's see how this looks like here.
So what we have here is we have a user card component, right? So not an employee card component, not a customer card, but a user card component that I guess just by looking at this is supposed to encapsulate both entities, both employees and customers. And if you look here, that's kind of what this component is doing, right?
So here we're passing employee data as the data property of the user card, then we're specifying that the type of this user card is of type employee. And then we're passing here all of these properties to show the header to show the footer, the statistics, and so on. And then also some custom actions, some configuration, a theme layout, and optionally, a class name here.
And then we have another instance of the same user card component. So here we see a great example of something that is reusable, right? Use the same component multiple times.
But here, we're passing customer data as the data property. And we're specifying type customer and then passing all those same props, obviously specific to the customer, but to the same component. Now, here's the thing, right?
Are you going to tell me that this is bad code, right? Is anyone any developer going to come in here and say that this is bad code? Probably not.
Because when you look at this, this isn't necessarily bad code, right? We're following all of the best practices and design patterns in react, right, we're following the methodology, the mindset that react wants us to follow by creating a custom component, and actually using it in multiple places. But now let me show you if you actually come here to the user card component, let me show you some of the sacrifices that we had to make to actually get this working.
And then I'm going to re ask you the question and tell me if this is the best way that we could have done this. So in here, if you scroll down here to the actual user card props, let's see what they are. So first of all, we have this data here, right, which we passed employee data and customer data into.
What we did here with this type is we put this as an any type. So we lost here some type specificity. Instead of making it either an employer or customer, we put any here to kind of encompass both types.
Now we know what some of you are saying you could easily fix this by doing something like employee over here. And then customer, right, you could do it like this, right and have both of these types here. And I would say that that is a better solution to putting any here.
But you're still missing the main problem of this. The main problem is that now you have to specify every time that you want to add a new entity to this user card, you have to specify it here in a type and also in the data, sorry, and then also in this type property here. So let's say for example, we also had over here something like admin, right, and we put admin over here, right now we have three different entities that this user card can represent.
And we have three different types as well. And are you going to tell me that there's not significant differences between an employee, a customer and an admin, right? There's not any permissions checks or any sort of extra logic that we might want to have on the admin, for example, if we did it this way, like this component is, we now have to do all of that inside of the user card component and make this component receive all the props that it need to essentially make sure that it works correctly as it's supposed to in all these different cases.
And if you scroll down here, you're going to see exactly what that is starting to look like. So over here, we have this render header function, which is local to this component, it just renders the header, which is a good thing, you want to see this in your components, right. But then if we look here at the actual JSX, what it returns, we're checking here if type is employee.
And if it is, we're rendering this part over here. If it's not, we're rendering this, which is applying to a member to a customer, right. But now if we have admin, we're also going to have to add the actual admin check over here as well.
And this is just going to complicate things directly, because now we have to make all of these checks and make sure that no matter where this component is being used, it's going to work correctly. And this is actually going to introduce a lot more work, right? Because now every time that we need to put this in a new place, we have to add more props to it, we have to mix and match more props.
And you can I promise you, you're going to get a bunch of edge cases that you haven't thought about. And you might even get some impossible states where you render something for an admin, for example, when you're on the employee page, just because the props are not going to figure properly. And it's a mess to kind of figure out how to set them properly.
So already, you can see the kind of sacrifices that we have to make the actual downgrades in developer experience that we have to make, just because we wanted to make this reusable. And we wanted to follow the way that react was supposed to be used, right. And that's exactly what I mean, right?
Like, this is not bad code, nobody's gonna say that this is necessarily bad code, right? Because we are trying to follow react in the way that it was supposed to be used, but it comes with some sacrifices. And then if you scroll down here, we're doing similar things in all of these functions in the render stats, for example, right, we're also checking here the actual type.
And if it's an employee, we're rendering projects, otherwise purchases, right, these are two separate things, they should not belong at the same component, right? Why are we putting all of these things here, right? We're doing it here as well with the employee as well, right.
And we're doing it in all of these other functions here, checking the type here as well, right, this is going to become very complex very quickly. And it's not the way that I would recommend that you do things. Now what I do like about this component, and one of the reasons why it's not so black and white is if you look here at the actual body of this component, this is actually really great.
I love to see that in an application. These you have all of these separate functions, right, that kind of act as separate components, which is totally fine to do it even this way through a function, because a component in react is just a function. So this is the same.
And because we're doing it like this, we also now have the flexibility to say, for example, I don't want to render the header, right, so I can just kill it. Or I want to put the header over here. And I can just do that super easily, without having to deal with like super messy code and trying to find my way around.
This is really, really good, right. And we like this, and we want to see this in actual applications. So we've established that this is good code, this is okay code, and it's code that you could reasonably ship in production if you wanted to write.
So now, let me actually show you better code, a better way to do the exact same thing, but just a way that is going to reduce all of these actual problems that we have. And instead, give us a lot more of the benefit benefits of reusability, but only when it should be there, not applied generally across the board. Now, before we continue, I just want to take a very quick moment to really think about what it is that we're actually doing in this video, because what we're doing here is something that is called learning by doing right, you're not just watching a traditional lecture video, you're actually watching me build this entire memory game, and you're learning by seeing me how to do it.
And then you can do it yourself. That's learning by doing, which is great, because that's exactly what brilliant, the sponsor of today's video is also doing. Brilliant is a place where you learn by doing you have 1000s of interactive lessons in various topics such as math, data analysis, AI, and of course, more importantly, for us programming, brilliant has a learning platform that is designed to be uniquely effective.
Their first principles approach helps you build understanding from the ground up. Each lesson that you have on there is filled with hands on problem solving that actually lets you play with the concept. This is a video by Darius Cosden, from the company called Cosden Solutions, and the audio critical thinking skills through problem solving instead of memorizing.
So while you're building real knowledge on specific topics, you're also learning how to become a better thinker. And they have a ton of courses to choose from. One of my favorites is one that is called Thinking in Code, which pretty much follows what we're doing here in this video and teaches you solid foundations for computer science that are going to directly help you as a React developer.
To try everything that Brilliant has to offer for free for a full 30 days, visit brilliant. org slash Cosden Solutions, scan the QR code on screen, or simply click the first link in the description, you're also going to get 20% off an annual premium subscription. Now to do this, I'm actually going to use AI just to kind of refactor everything so that I don't have to do it myself.
And I think it's only fair because nowadays AI pretty much codes like 50% of my code. So it should only be fair that it tries to like now creep into my videos as well and help out a little bit. And this is also going to be a good opportunity for you to kind of see how I use AI and maybe get a few tip or two from that.
What I'm going to say here, as I'm going to tell it first, give it access to my entire code base. I'm using cursor, by the way, if you're curious about that, and we're just going to say refactor user cards. And actually, I'm going to give it to user cards.
So it knows exactly what I'm talking about refactor user card to have separate components for the employee and the customer without complex props being passed around to configure everything, right, because we want to essentially keep things simple, right. And here, what I like to always tell it is write senior level code and keep things super simple, because sometimes AI likes to just do a lot of different things. So I'm just going to do that, I am going to come here and just remove this admin stuff, because if I don't, it is actually going to do the admin stuff itself.
And I'm going to remove here and put it as any like we had it before to see exactly how it behaves. So now let's enter this and let's see what it comes up with. And then I'm going to kind of take over, just fix it up a little bit if I need to, and show you the correct way of doing things, because the AI is perfectly capable of figuring this out, if you give it the actual right instructions.
Okay, so it's done. And now I'm just going to blindly accept all and hope for the best, let's see what it came up with. So let's start looking at the app component.
So already, I think I can see that it got it exactly correct. So our imports here at the top, we no longer have user card. So that's killed, we no longer need user card.
Instead, we have customer card and then employee card. Perfect. Now we still have our same objects.
That's exactly like we had before. But now we have two components here, we have employee card, and then we have customer card. And as you can see, we're only passing the data here to the employee card that belongs to the employee.
And then we're only passing the actual props that are related to an employee to this specific employee card. And we're doing the exact same thing over here with the customer card. So now we no longer have to worry about passing all of these props, making everything reusable, it's much simpler to just have separate components for each individual entity, and just go that way.
And then if we look here at customer data, this is by the way, the first time that I'm actually going to look at this at the same time as you, we have here a type for the actual data of the customer, that's perfectly fine, we could maybe put this in a separate file, we have here the props, right, which again, are only related to the actual customer. And then over here, right, very importantly, I like that the AI did this, we have a base card over here, right, and we're going to look at that in just a second. But now here, we have all of the code that is specifically related to an employee to a customer, sorry, and nothing else, we have nothing about an employee here, we just have everything about a customer.
And this makes a lot more sense. Now there's something to be said that we could put all of these in separate components if you wanted to. And maybe we're going to do this as a separate step with the AI because it's super simple to do.
But the concept remains, this is now simpler to work in and to edit and to kind of use, then if everything was combined into one super component, if you look here at the employee card, it's pretty much doing the exact same thing, a type here for the data type for the props. And then we have pretty much the same thing over here, right? Again, the same flaws, it's not in separate components.
But this essentially having two components like this is better than having just one reusable component. And if you're thinking now that we kind of went against react, and we're not anymore having reusable components, we didn't because we only now as you can see here, we only created reusable components for the things that we actually need to be reasonable, like this base card over here, if you go and open it up here, we just have a diff, right? It's again, super simple.
This is just an example. But here would go all of the things that are common to both cards to both customer and employee cards. Here, it makes sense to make a reusable component.
This is actually the way that react wants you to use it make things that are reusable, but only for the things that it makes sense. Don't try to put everything reusable, because then you're going to shoot yourself in the foot and you're going to overcomplicate things a lot. So now if I ask you this question, again, is this good code?
Well, it definitely is good code because we haven't regressed from the previous code. It's not worse than the previous code. But this is now better code because everything is now organized into a separate component.
And we can even make this a little bit better, we can actually ask a follow up over here. So we can tell the AI inside both components, we factor to create sub components and put them in the same file as the main component to be more senior to be more encapsulated, and to be easier to understand. Let's see what the AI does.
Now what I'm basically telling you to do is great, we have customer card, it's really great. But now we can split this even further, make sub components as well and just organize things a little bit better. So let's see what it comes up with.
And once again, we're going to blindly accept what the AI is doing. I don't recommend that you do this unless you absolutely know what you're doing. In my case, I know what I'm doing.
And I always look over the code eventually in the end, and I'll update it. So it's fine. But if you don't do this, maybe just be a little bit more cautious before blindly accepting with AI because that is I promise you going to introduce a whole different set of problems.
So let's look at customer card once again. So over here, now we have a header component, right, which was very similar here to this user card component where we had a render header. Now it's a separate component, right?
Same concept, just a little bit better, right? And all this component receives here some props as well, which has some of the props of customer data, just handles rendering the header. And this is, again, specific to the customer card, we have category list, same thing, we have details, same exact thing, we have stats, same exact thing.
And now we have here the customer card component. And look at this, we have base card, we have the header, we have the details, we have the stats, and we have the actions. This is how React wants you to use it.
This is composability in React. Here, it makes sense. We're passing just a few props, all the props that we're passing makes sense.
There's no other better way that we could do things. And this is generally a better approach than just having everything in a user card. And if we look here at the employee, I highly suspect that is going to be the same thing, header skills list, and so on.
It's exactly the same thing. One could argue that now because we have these separate components here, we could potentially make things make these reusable if we wanted to. But again, maybe not because this one takes in years of service.
And this one in the customer, which one was it was the details component. Let's see where it is details. This one takes in last purchase date.
So it can't really make these two reusable because it's related to the different data, right. But if they had similar data, just like this base card over here that could be used in both places, then you make it into a reusable component, then you reuse it in different places to not repeat yourself and write the same code over and over again. So really, I hope that you're getting things now.
I hope that you understand the differences and the nuances here. And I really hope they understand what I said in the beginning of this video, that this is not about bad code versus good code. But this is rather about good code in a nuanced way, and then better code.
And I really hope that this was clear in this video, and that you actually get this because this is, in my opinion, very, very important. If you enjoyed this video, and you want to see more videos just like this one, make sure to leave this video a big thumbs up. You can also click here to subscribe or you can click here to watch a different video of mine that YouTube seems to think that you're really going to enjoy.
And with that being said, my name has been Darius Cosden. This is Cosden Solutions. Thank you so much for watching, and I will see you in the next video.
Ciao, ciao.