Identifying Repeated Patterns while Dynamically Generating the Channel List

Sam Selikoff
InstructorSam Selikoff
Share this video with your friends

Social Share Links

Send Tweet

See how to dynamically generate our lists of channels using a React component and Tailwind's spacing utilities, and learn how this is often enough to reduce any duplication we have in our templates.

The code for this lesson is divided into two sections, with this you can see the progress on the code.

  1. Begin
  2. End

Instructor: [0:00] Now that we have two channels here, let's keep working on filling out the rest of this sidebar. We're going to start with this Tailwind CSS category title. Our current div that wraps our first two channels is right here. We can start with a new one right down here.

[0:15] We'll make this category title a button and we'll call it Tailwind CSS. We also have this Arrow icon right in front of it. If we come over and take a look at this, we'll see this SVG is 12 by 12. We can slap on a width three and height three just like that.

[0:37] We'll make this a flex and item center. Our button's rendering. If we take a look at the typography here, we'll see that this is 12 pixels. Actually, it's our Ginto font, which is our title font that we added earlier in the course.

[0:54] We can come to our button, use text extra small for 12 pixels. We'll use font title to get Ginto. That's looking pretty good. These category titles are actually all in uppercase and Tailwind has a class for that. It's called uppercase. That takes care of that. Let's work on the spacing here. Looks like we want to push it down a little bit.

[1:23] Let's just start with a little bit of margin top here. Maybe MT4. Looks like a little bit more. MT5. Let's throw a little bit of padding on this, maybe PX1 for padding in the horizontal direction. That arrow is over a little bit too far. Let's switch this down to .5.

[1:52] It looks like we need to push it down just a tad more. 5 is 20 pixels. Let's go with 21 for now. Now that arrow looks like it's lining up perfectly. We need a little bit of margin right on our Arrow icon. One looks like a little bit too much. Let's try .5.

[2:13] The beginning of the T in Tailwind is lining up great, but you'll actually notice the text here feels a little bit more stretched out. That's actually a typographic technique that affects the letter spacing.

[2:25] Well, Tailwind has a class called tracking which we can use to adjust the letter spacing CSS property. We have six default values here. Let's go ahead and make this just a little bit wider using the tracking wide utility class. We can see that stretched out these letters a little bit. That's looking really nice.

[2:49] Now we want to get these next four channels in. These have the same treatment as our first two. Why don't we grab this div with the spacing, come down right below our button, and just paste this in. These are going to actually use the Hashtag icon. We'll grab that.

[3:14] We'll change this to general plugins, and then we'll make two more copies for help and internals. Those new channels are rendering. We see we need a little bit more vertical space right here. We'll just come to this. Let's try MT1. Little bit off.

[3:40] Let's try five pixels and see how that works. That is pixel perfect. What I'm going to do now is open this data.JSON file that I put in the root of the repo here. This contains all of the categories and channels in the actual Discord.

[3:59] We're going to be able to render our UI from this data, simulating fetching data from a backend server. We can just pop up to the top here. Let's go ahead and get rid of this router and useRouter, and import data from data.JSON.

[4:18] What we want to do is identify the repeated patterns. We'll ignore welcome and announcements for now. Basically, every category we have, we have this title, and then we have the channels. We see that repeats a few times here. That all starts with this div.

[4:34] Taking a look at data, why don't we actually bring this right down here, just so we can see. We'll see, I put the data under this one key because we're on server/1. I'm going to come right here and we can say data1.categories.

[4:57] This is going to give us each of the category names. We see the first one has no label, but then we have Tailwind CSS, Tailwind Labs, and so on. The way we loop over data in React is to use .map. This will give us each category. Then we can use an Arrow() function.

[5:18] At this point, we basically just want to move this whole chunk of HTML right inside this loop. We'll go ahead and give this div a key of category.ID. Now we can replace this with dynamic data, so each category has a label.

[5:36] We'll say, category.label. Then here where we render all the channels for that category, we can go ahead and grab those directly off of each category because there's a channels property. Inside of this div we'll say, category.channels. Then we'll call .map again.

[5:56] This time we have a channel. Then we can just slot one of these A-tags in just like that, and we'll get rid of the rest because we don't need these anymore. Let's go ahead and key each anchor tag by the channels ID, and replace the text here with channel.label.

[6:22] Now we can see all of these channels are rendering from the data. Let's go ahead and get rid of our first two because we should be able to repeat these using our loops down here.

[6:36] We see that we're rendering an empty button because the first category doesn't have a label, just like it doesn't in Discord. We can just conditionally render this button in React by checking whether we have a category.label. If we do, we'll go ahead and render a button.

[6:58] We've got the channels here, but we have a little bit of a spacing issue. You'll see, we were using this margin top 21 pixels to take care of the spacing in between each category right here, and right here, and so on.

[7:13] We want to use the same technique we already used, where we use the space Y utilities, since that only puts space in between siblings and not above the first one. Let's come up to here, where we're wrapping each one of these categories.

[7:28] We'll just throw a space Y 21 pixels, just like that. That means we can knock it off of this button. That took care of that little bit of margin. We still have this margin 17, which looks to be a little bit too much. Let's bring this down 14, 12. 12 seems to bring it back in, right in place.

[7:55] That means we can just replace this with MT3. All of our channels are lining up pretty perfectly here. The only thing we lost was these unique icons for the "Welcome and Announcements" because we're just rendering this Hashtag icon in our loop.

[8:12] We'll see down here in our fake data that these two channels have this custom icon property, which we can use to render a different icon. In order to do this in React, we're actually going to change how we import these icons.

[8:25] Instead of importing them all individually like this, I'm just going to delete these and say, import star as icons from our component icons. This is going to give me a single map or object here, where the icons are all keys.

[8:47] If I go and find all of these icons and just say, icons.verified icon, we'll see that that properly renders our icons again. This is just nice because it's just a native JavaScript way to import basically a collection of named imports from a module.

[9:06] Now that we have this icon's namespace, the extra icon feels a little redundant. Why don't we just clean this up with a quick refactoring? We'll change these to remove that suffix and then we can grab all of these Delete icon from the end.

[9:23] We'll also pop over to our _app, where we're using the Discord icon, and just change this to Discord for now. We have this nice little API. We can just say, icons.IconName to render an icon. This is going to help us with our Dynamic Channel icon.

[9:42] What we want to do, just close this, is move this channel link into its own component. Function channel link. We'll just come up and grab this HTML, return it from our channel link, and render a channel link instead.

[10:05] We can see that this channel link needs the channel information here. We'll pass that in as a prop. Then we'll go ahead and key this again by channel.ID.

[10:21] This component now gives us an isolated space where we can actually use a variable called icon to set up a dynamic icon for each link. If the channel, which is the JSON coming from the server, has an icon property, then our icon will be icons channel.icon.

[10:42] Otherwise, we'll use the default of icons.hashtag. Instead of rendering this icons.hashtag, we'll just render our new dynamicIcon component. With any luck, we should see these two book and speakerphone icons, which we do. Look at that.

[11:04] We have this whole sidebar filled with dynamic data matching just great with Discord. Looks like we have a little bit of a scroll issue here. Probably because we're using margin. Let's go ahead and switch this to padding. That should take care of that. This is pretty neat.

[11:24] I just want to step back here and take a look at what we ended up with. When we were starting this, we had a lot of duplication with all the channels that we wrote, and your spidey sense might be going off saying this is a lot of CSS classes, a lot of duplication.

[11:40] We should probably start extracting some classes or extracting some components. As you saw, just through the process of generating this UI dynamically from server data, a lot of times you'll just take care of the duplication without even thinking about it.

[11:57] One thing I really want you to take away from this course, is that if you're new to Tailwind, you might have an urge to dry up your code to remove that duplication prematurely.

[12:10] You might look at some of your templates, see a lot of classes, and feel like it's going to be hard to maintain. After just a few days or weeks of working with Tailwind, you'll actually find a lot of duplication takes care of itself.

[12:22] Having all these classes co-located with the templates and components that are using them, keeping there, not extracting them out to a separate file or a separate abstraction, actually makes your code way more maintainable.