Learn React With This One Project
Chapters18
Introduces the video as a React walkthrough for building a complete habit tracker, with emphasis on understanding each React concept as you build. Also mentions a related React course and project scope.
A hands-on, project-driven tour of building a React habit tracker with Tailwind, TypeScript, and advanced concepts like context and localStorage.
Summary
Web Dev Simplified’s Kyle walks you through building a fully functional habit-tracking app in React, using a single project to illustrate core concepts. He starts by scaffolding a Vitest-based React + TypeScript setup, then introduces Tailwind for styling and a custom Button component to demonstrate props, children, and variants. The video deep-dives into component architecture (header, form, list, habit item), state management with useState, and lifting state up to a shared parent. Kyle also covers rendering lists with keys, mapping arrays to JSX, and handling events like add, delete, and toggle completions. He extends the example with date handling via date-fns, derives UI state from props, and demonstrates derived state versus local component state. A significant portion is dedicated to refactoring with context to avoid prop drilling, plus building a custom hook for localStorage persistence. The takeaway is not just the UI, but a practical React workflow—from creating reusable components and props to managing complex state and persistence. If you’re hungry for a deeper React dive, the video also plugs Kyle’s React Simplified course (sale active this week) and a free React Hook Simplified course as additional learning paths.
Key Takeaways
- Scaffold a Vitest + React + TypeScript project in the current folder, selecting React with TypeScript and a compiler for optimization.
- Tailwind CSS is wired into Vit; you import Tailwind in a dedicated CSS file and reference Tailwind classes directly in JSX (e.g., className="text-3xl font-bold").
- Create reusable components (Header, HabitForm, HabitList, HabitItem, Button) and feed them via props, including children for inline content.
- Use state (useState) for local form input, map arrays to JSX for lists, and provide unique keys for each list item to satisfy React’s reconciliation.
- Introduce context with a HabitProvider and a custom useHabits hook to avoid prop drilling, making state like habits, add/delete/toggle accessible anywhere in the tree.
- Persist state with a custom useLocalStorage hook and useEffect to sync changes to localStorage, including a date reviver to properly parse date strings.
- Demonstrate derived state vs. stored state by computing visible dates and week navigation outside React state when possible, avoiding unnecessary re-renders.
Who Is This For?
Essential viewing for React developers who want to learn through building a real project, including how to structure components, manage state, and use context for scalable state sharing. Great for learners who want practical patterns beyond toy examples.
Notable Quotes
"'I'm going to show you how to build out this fully functional habit tracking application that does everything you would expect from a normal habit tracker.'"
—Kyle sets the goal of building a complete habit tracker and explaining the major React concepts behind it.
""We can render out my habit item with a custom component and use props, including children, to customize what’s shown.""
—Demonstrates component composition and the power of props and children in React.
""If you want to learn more, my full React simplified course is currently on sale for this week.""
—Promotes the course while teaching core concepts.
""Always make sure you add a key property and make sure it’s unique to that particular element.""
—Important React best practice for rendering lists.
""Context is a way for you to wrap a bunch of components and then give them access to specific state data anywhere they want without prop drilling.""
—Explains the motivation and benefit of React Context.
Questions This Video Answers
- How do I set up a React + TypeScript project with Vit and Tailwind?
- What is prop drilling and how can React Context help solve it?
- How can I implement localStorage persistence for a React app?
- How do I create reusable components like Button in React with a variant prop?
- What is a custom hook and how do you build one to manage localStorage in React?
ReactTypeScriptTailwind CSSVitReact ContextCustom HookslocalStoragedate-fnsProp DrillingHabit Tracker
Full Transcript
If you've been wanting to learn React, this is the perfect video for you. I'm going to show you how to build out this fully functional habit tracking application that does everything you would expect from a normal habit tracker. But I'm not just going to be showing you the lines of code to write. I'm also going to be explaining what every line of code does and going in-depth into every single major concept of React. So that way by the end of this video, you not only have this full project built out, but you also have a strong understanding of React, so you can build any of your own projects as well.
Also, if this way of learning sounds appealing to you, my full React simplified course is currently on sale for this week. So, I highly recommend checking it out. It's over 40 hours of literally every single thing you ever need to know about React. And at the end of the project, we build out a fully functional job board that includes authentication, payments, and so much more. This is the ultimate React course if you really want to land a job as a React developer. So, if you enjoyed this video, I highly recommend checking this course out. It'll be linked in the description, and it's currently on sale for this week.
Welcome back to WebDev Simplified. My name is Kyle and my job is to simplify the web for you. And the very first thing we need to do to create a project with React is to type inside the terminal npm create vitest. And then what I like to do is I just like to put a period after this command and that'll create my project in the current folder I'm in. But if you wanted to create it in a specific folder, just type in the name of that folder. In our case, I want it to be in the current folder I'm in.
What this command does is it essentially spins up vit which is a bundler that essentially allows you to write all of your React code and then it converts it to something that the browser can properly handle. Now in our case we want to scroll down to the correct framework which is going to be React. We can select that by just hitting enter. And then you can see we can select a bunch of different varieties and your list may be slightly different than mine depending on when you watch this video. But in our case we want to use TypeScript specifically with the React compiler.
Now, the compiler is not really something you have to worry too much about, but it's just something in there to help optimize your code in React. So, that's why we're choosing the compiler version. Now, when we do that, we can just say we want to install it with npm and start it immediately. And it's going to go through install all the different dependencies that we need and then start up our application. And it'll show us on the right hand side of our screen that application when it's actually done being pulled up. And if we take a look at the starting code for this application, it's relatively basic.
Now, that looks like there's a lot of code, but you can pretty much ignore everything that's not inside this source folder because the source folder is where all of your actual React related code is going to go. And everything outside of here is just setting up things like your llinter with ESLint and setting up your bundler with this V config file. Now, that's done. You can see that right here we have this URL we can open up. And it's just going to give us the starting application for our page. Again, yours may look slightly different depending on when you're watching the video, but it doesn't actually matter what this looks like cuz we're going to be deleting everything inside this starting application.
Now, real quickly going through the actual starting code, I'm just going to open up this source folder. We have this assets folder, which just has some of the images that are being used on the page. We can remove this because we're deleting all the starting code. We see that we have this CSS file here. Again, that's all just for the default rendering. So, we can delete that since we don't need it. We have this other CSS file here. Again, we're not going to be using this, so we can completely delete this file as well. And then finally, we have this main.tsx and app.tsx.
And this is where all of our React related code goes. So anything in React is going to be a JSX file. And since we're using TypeScript, we're using a TSX extension. And that allows us to write this like angle bracket style code that looks a lot like HTML directly inside of our JavaScript. That's what JSX is. And that's essentially what entirety of React is built on top of is this JSX syntax. And again, since we're using TypeScript, we use thetsx extension. Now, to finish up getting our page to work like we want, since we removed that CSS and those asset files, we can just delete everything inside this file.
And instead, we're going to just say export default function and we're going to call this function app. And by default, we're just going to return null from here. So, it should just give us a completely blank page with that file being saved. And then inside of here, that index CSS file, we removed that as well. So, we're going to delete that from this file. And now we just have a completely blank page. And if we wanted to, we could write something in that page. So, for example, in this app tsx again, since we can pretty much write anything we want that's normal HTML, I can just put some random text in an H1.
And you can see that automatically shows up on my page and everything is automatic. When you save, it automatically refreshes and puts all your content on the right hand side wherever your browser is. So, you get automatic refresh, which is really nice. And again, since we have this JavaScript syntax, that's JSX. We can write our HTML code essentially directly inside of our JavaScript with very small changes that I'll cover as we get to those particular points. Now, for our particular project, I'm going to be using Tailwind for all of our styling. It's okay if you don't know Tailwind.
It's not important that you do. It's just going to be what we use for our CSS styling. So, if you go to the Tailwind sites, there's a section on using it with Vit. We just want to copy over the command for installing Tailwind. So, we're going to paste that down to install Tailwind. And then, we need to configure Tailwind inside of this Vit config file. That's the file I told you right here that sets up all of our bundling. You can see we have React as well as the React compiler because that's the presets we chose.
But, we just need to add in Tailwind CSS. So, let's copy over the import for Tailwind CSS. And let's make sure we use Tailwind CSS inside this plugins array, just like this. And now, if we give that a quick save, we now have Tailwind installed into our application. The only thing we need to do is to make sure in some CSS file that we import this Tailwind CSS. So, let's create a brand new file. We'll call this index.css. And I just want to import that Tailwind at the very top. And then in my application, this is something that's kind of interesting about React and a lot of other bundlers.
The way that V works is if you just import a CSS file right here. So we can import that CSS file that we just created. It'll actually find that CSS file, compile it, and put it into your page properly with a link tag and everything else. So you don't have to manually do that yourself. So now we have that CSS file in our page. And if we look over here and we just refresh our application by restarting it by just running npm rundev, that'll start up our application on that exact same port. And now if we refresh, you'll notice our font size has been drastically decreased.
And that's again because we are using tailwind CSS which automatically gives us the ability to style things using those classes. So inside of JSX instead of writing class and then putting the class you actually need to write class name that's just because class is a keyword in JavaScript. So you can't use it directly. So if we write class name followed by any class names we want. So in our case we could change the color of this which is just text to like red 500. And now you can see that our text has changed to a red color.
So we just have Tailwind properly hooked up with our application. everything automatically refreshes. And again, since we're using React, we need to make sure we write class name instead of class. And that's all we need to do to get started with setting up Tailwind and our application. So now we can finally move on to actually creating the different parts of our application. And the first thing I want to focus on is what you need to think about when you're a React developer. Let's take a look at the actual current working version. Let's zoom this back out to more reasonable zoom level.
And you can see that we have a few different sections of our application. We have this nice header section which has a title. It tells us how many different things are done. It also has the ability to navigate through our dates. So you can see as we navigate our dates, the rest of our application changes. We have this next section which is like this form where we can type in some name for new habit. We can click add and that new habit is being added to the page. And then we also have the ability to delete things inside this list by clicking this delete button.
So that's kind of the main functionality. Also the next section of our application is this list. So you can see it lists out each one of our habits inside this list. And each habit itself is kind of its own element of the page because you can see it shares the exact same pattern between every single habit inside of our list. So we can essentially think of this as three different sections in our application. We have the header which contains all of our date information and the title. We have a form for adding new habits. And then we have a list of each one of our habits.
And when you're working in React, you want to break your page down into these different sections. And they are generally called components because React is a component-based library. And every single thing we build is going to be a component. So, we want to have a component for this top section, a component for the form in the middle, and then a component for the list of each one of our different habits. And we're going to put all of that directly inside this app page right here. Now, this app page is essentially the starting point for your application.
Technically, this main file is the starting point, but almost always the main file will import your app and nothing else. So, the app file is kind of your main starting point for your entire application. And inside here is where I want to create that layout. So, we're going to have a div that wraps our entire thing. Let's make sure that's on a line like this. And we know that we have a header section, we have a form section, and then we have a list of each one of our habits. So what we're going to do is we're actually going to create our own custom components for each one of those.
And the way custom components work is they're just a function. So in React, you just type a function and then you want to give the function a capital letter name. So it's going to have a capital letter as the first part of the name. And that is different than normal JavaScript functions. And it's actually required for it to work properly with React. So we have a header for example. So we can call this header just like that. And now inside the header, we can return whatever content we want and we can render that out. So let's just put an H1 that says header directly inside of here.
And now I can use that component inside of this section by using the exact same angle brackets I would with HTML, but by putting my custom component name just like that. So now I've created a header component and it's going to render out whatever I put inside of here. So if I look at my application, you can see here it's rendering out that text header because that's what's in my component. And if I change this, you can see it renders out header 2. So essentially it just takes the code in here and paste it directly wherever I put that component inside my application.
Now we obviously want to do much more inside of our header. So let's go ahead and actually see what our header is going to look like and start working on the code for this header section. So the first thing with my header is it's essentially broken into two sections. I have this left-hand side and this right hand side. Easiest way for me to lay that out is with flexbox. So we'll come in here and we're going to use a div. And actually I'm going to use a header because it's more semantic HTML which is obviously better.
There we go. And inside here I'm going to have two sections. The first one is going to be a div on the left and the second one is going to be a div on the right hand side. So one is going to take care of this and one is going to take care of this. And to make sure I separate those elements out, we're going to come in here and we're going to use flexbox to separate them. So we're going to say flex. We're going to put the items in the center and we're going to justify content between.
And that's just going to make sure that it center aligns these elements and pushes them as far away as possible from each other. Now the next section is going to be my lefth hand side which again I want to use flexbox but I'm going to do flex of a column. So it's a vertical flexbox and then I'll come in here with a gap of one just to space these elements out a little bit from one another. Now inside of here is where I want to put my habit tracker text as well as this one out of one content done.
So I can come in here with an H1. This H1 is going to say habit tracker just like that. And then below that I'm essentially going to have some text inside of a span that's going to say like one out of one done today. That's these two elements of our page. Now, let's go ahead and actually style what they should look like. Because right now, if we save this and we look, you can see we have our text, but it has no styles, which is obviously not ideal. So, for our H1, let's come in here with a class name.
We're going to use the text 3XL to increase the size of it. And then we're going to come in here with a font of bold to make that a bold font. So, now you can see it's a much larger bold font. And for our one out of one, we can come in here with a text zinc of 400 and a text of small to make it slightly smaller and more gray. And now you can see that that looks exactly like we want it to as well. Now we should probably work on the background color and everything else for our application.
This can actually be done in our CSS file because we want to select the entire body element. So I want to specify my background color and we can just use the variable which is color zinc 900 that comes from Tailwind CSS. And then for our color of our text, we want to change that to a white color. So we'll just say color zinc 100. And that's going to give us a white color by default. And there you go. You can now see we have that background set up. And everything overall is looking quite good. What we want to do though is add a little bit of spacing from our elements up here.
So in our main app, which is the thing that wraps our header, let's come in here and add a maximum width of 2 XL so it's never too large on our screen. We're going to add some margin on the right and left of auto. That makes sure it centers itself on large screen sizes. And then we're going to add a little bit of padding on top of bottom. And then we're going to stack everything in a vertical direction with a gap between them. So there we go. That nice and centered our text in the screen by giving it a little bit of padding.
And if we had a very large screen size, you can see it doesn't ever expand past a certain width, which is exactly what we want. So, let's shrink that back down and let's go ahead and finish out what our actual header is going to look like. So, inside of our header, the next section that we can work on is going to be the right hand side. So, let's look at what that looks like. You can see we have this text for the dates as well as two different buttons. So, what we can come in here is add those little flexbox classes.
So, we'll copy over the exact same flexbox to this section over here. So, now you can see we have that vertical stack going on. And inside this vertical stack, we're going to have some text in a span. That's just going to be our dates. So, we can say like April 6 through April 12th. Just like that. And again, the text on this is going to be pretty much identical to that one out of one done. Exact same font and exact same font size. So, if we give that a save, look over here, you can see that it's already showing up and it's centered, which is what we want.
Next, we need to add some buttons. And these buttons need to be stacked horizontally. So, we're going to wrap them in their own div. Make sure that they are centered and with a gap of three. Just like that. And now we can put in each one of our different buttons. So we're gonna have a previous button and we're going to have a next button. And if we go ahead, you can see we have our previous and next button. And since we're using Tailwind, they have no styles by default. Now, we could manually style what these buttons look like in line right here.
But if we look at our application, you notice we have a lot of buttons and they all share almost the exact same style. They have the same kind of shape, the same color, the same font size. Everything is very similar about them. And in React, whenever you have something that is similar across your entire application, it is almost always better to extract that out into a custom component and then reuse that everywhere in your application. So instead of manually writing out our button code for every button and copy and pasting it, we're going to create one single button component that encompasses all the buttons in our application.
So what we can do inside of a file here is we can create a brand new file called button.tsx. And I might as well just put that in a folder called components. This is where all of our different components are going to live. And then we can just say we want to export a function. We're going to call this a button. Just like that. And now we can render out our button code inside this section. So let's just copy what we have inside of here. I want to return this as is. And now I can replace this with that button component that is custom.
Just like that. And now it's going to render out my button. You can see it's exactly the same as before. I can do that for both of these sections. And now you can see both my buttons are rendering out. Now I do have a little bit of an issue. You can see they're saying previous and previous. That's cuz I currently have the text previous hardcoded into my button. Now, I could change this to next, but again, it's hardcoded as next. I want to be able to pass this along into my component. This is where the idea of props come in.
Props are something that you can pass from one component to another component. It's essentially like passing data to a function. So, in order to pass along props, we go back to the thing we want to pass the prop to. In our case, we could pass along what the text for this is going to be. We can just say text. And then you give it any value you want. In our case, let's give it the value of previous. And for this other button, we're going to pass in text and give it the value of next. So now we have these pieces of data that we can use as essentially properties to our function.
Right now we're getting errors because there are currently no props being accepted. You can see this function takes no arguments. The way that props work in React is they always pass along an object. And this object is generally just going to be called props or something along the lines. And then we can give that a type. We'll call this our button props. So let's create that type button props and essentially this is always an object like I said. So we want to define this as an object and then put each one of our properties inside of here.
So we have a text property which is a string just like that. And now you can see in props we have that text property. So I can come in here props.ext and get access to that text. And you can see here all my errors have been removed. And if I just go and I replace the text with this props.ext it'll put the text in place. Now, obviously, if I just paste my text like this, it actually renders out props.ext. It doesn't render the actual text element. In order to render actual JavaScript code in your React components, you need to wrap them inside of curly braces.
By wrapping inside of curly braces, essentially you're telling React, run the code inside of here as if it was JavaScript, and then whatever it returns, put inside of the actual HTML output. So, now you can see by accessing props.ext, I get my previous and my next button showing up on my element, which is exactly what I want. Now, generally when you're working with props, I like to dstructure my props in line. So, here I will just write text like that. And then I can use my text directly down here. This makes it a little bit easier to work with my props because now I don't have to put props dot whatever.
I can just write the actual name of the thing to make it a little bit simpler and easier to work with. Now, there's technically a way that we can improve this slightly as well, cuz right now you can see we're passing the text along like this, which is kind of weird. I would almost rather just be able to pass my text in between my elements like I would do with a normal button. So, I'm going to show you how you can do that as well because it's actually very simple. Inside of React, there is a special property that's called children, and it's always available in every single custom component you create.
And essentially, children is just equal to whatever you pass in between your elements. So, it's whatever text you pass down to your element. So, instead of calling this text, I can actually call this children just like that. And here I can paste in children as well. And you can see immediately if I save that fixes my problem over here. It's now passing along that children. And this is like I said a component or a function inside your props that is always there and it's always available inside of React. Now to type this inside of TypeScript, it's called a React node.
So we can come in here with children and we can set the type to React node. That is the type of whatever is being passed along. Essentially it can be a lot of different things, but it's mostly in our case going to be just a string of text. So it allows us to take whatever we pass between our element and it takes it in as this special children prop that's automatically handled by React for us. So, you can pass along your own custom properties with your own name, or you can use the built-in property children to pass along specific text, for example.
Now, if you wanted to pass along other things, obviously, I could just come in here, pass in whatever I want, and I can pass that along. And you don't just have to pass along strings. If you wanted to pass along a value, just wrap it inside a curly braces. And now, I could pass along, for example, the number zero. And that passes along a zero number instead of the string of zero. In our case, we don't really need anything else. So, we're going to leave this exactly as is. And now we can actually style what our buttons are going to look like.
So here we want to specify a class name for all of our different classes. And in our case, we're going to have a background of violet. That's going to give us that purpley color. We'll use violet 600. And then we're going to have a hover, which is a background of violet. And this one's going to be violet 500. There we go. So now if I hover over those, it just becomes a slightly lighter color. And actually, I spelled hover wrong. There we go. So now when I hover over them, it becomes that slightly lighter color.
Now let's go ahead and fix all the rest of our content. For example, we want to add some animation. So, we'll do a transition on our colors to give us a little bit of animation. We'll make the button rounded. We'll add a little bit of padding on the top, bottom, left, and right. And then finally, if the button is disabled, we'll set the opacity to 30 so that it's a slightly more opaque color. And if it's disabled, we will set the cursor specifically to the not allowed cursor. So, let's find that inside of here. There we go.
Give that a quick save. And now you can see our buttons look exactly the same between these two examples. And when I hover over it, you can see that I get that nice hover effect. And if these buttons were disabled, for example, I just pass along disabled here. You can see that I get this nice grayed out color. So, it's working exactly like I expect it to. Now, there's one other small thing I want to change about my header. And that is that this right-hand section, I want all the items to be aligned on the end.
So, we'll just put items end here to move that date over. And now we have an exact match between these two different pages, which is exactly what I want. Now, the next thing that we can work on is this form section right here. So, let's scroll up. We have our header. Now, let's go ahead and deal with our habit form, which is going to be another custom component we create. And you'll notice something like I have these header components with no content being passed into them inside of React. If you ever have an empty element where you're not passing anything to it, you can actually do a self-closing element where you just put the slash at the end like this.
That's exactly the same thing as what we have down here. So, you can make it self-closing like this just to make your code a little bit more concise. And again, if we add that new function for the habit form, just like that, and we make sure that it returns something. Now, you can see all of our errors are cleared up and our code still works exactly the same as before. So, this is a nice little hack for writing more condensed, slightly easier to work with code. Now, another thing that's important is often times when you're creating components, especially more complex components, it makes sense to take them and move them into their own file.
So, I'm actually going to create a brand new file here for my header. And I'm going to take all the code. Again, remember it ends intsx. I'm going to take all the code for our header here. I'm going to move it into that brand new file that I just created. And what I want to do is I want to export this component so I can use it in other places. And just make sure I import my button component as well. So now I have all the exact same code. I can just make sure I import my component here and everything will work exactly the same as before.
You can see there's no changes on this right hand side. Now I also want to do the exact same thing with our form. So, let's create a new file called habit form. And let's come into our app, copy over all that habit form stuff, paste it into here, export it, and then make sure in here we import that component as well. And now we can actually work on creating what our form is going to look like. And luckily, it's a relatively straightforward form with just a single button inside of it and a single input element.
So, let's create a form element just like this. And inside of our form element, we're going to have an input. That input is going to be for our single text input. Again, we can use it as self-closing. And then we're going to have a button as well which says add habit. Just like that. And again, the reason we create these nice custom components is now just by adding that one custom component. If we look at our page, it automatically has all the styles for that add habit button, which is really, really nice and easy to work with.
Now, for our input element, we actually need to add quite a few class names. For example, we want it to grow to fill the full width. So, we'll add a flex one on here. And on our form, we'll add a class name of flex with a gap of two. That makes it so our input fills the full width. And now to make it so we can see our input. Let's add some rounded large on it. We want a background of zinc 800. We want some padding of four on the left and right and two on the top and bottom.
We're going to remove the outline cuz we're going to add our own outline. So we can say focus visible. We're going to add a ring two. That's going to be our own custom outline. And for the focus visible, we're also going to make sure that that ring has that violet color. So we're going to say violet 500. Just like that. And now if we give that a quick save, you can see we have our input. And when we click on it, we get that nice little purple outline around it, which is exactly the same as the content we have over here.
We can also add our placeholder text. So inside the input, let's add that placeholder text. And we can just say new habit. Just like that. Now, if I give that a save, we look over here, you can see I get that placeholder text as well. Now, if we compare and contrast these, there is a small difference in our button. Our button is slightly different between the two. We will actually come back and fix that problem in a little bit because it involves a slightly more advanced trick in React. But for now, I want to move on to styling this bottom section here for the actual list of items and habits that we want to work on.
So, if we go back to our app.tsx, we essentially need to have a habit list, which is going to be this section that lists out each one of our individual habits. And again, let's create a brand new component for that. I'll just copy our form over, rename this habit list, and call this habit list. And we'll just get rid of the content inside of here. So it just returns null. There we go. And now if we come into our app, we can import that habit list component. And now we essentially have that. And we can put whatever text and content we want inside of here to make it render down below.
So in our particular case, we want to have essentially an empty state and a state for if we have elements. So let's just create a placeholder variable called habits. We'll set it to an empty array. So we can start out by seeing what happens if we don't have any habits. So I can just have a simple if check here. I can say if our habits.length is equal to zero, well then we want to return something else. Let's say just return an H1 that says empty. Otherwise, we'll return an H1 that says full. Just like that.
So now you can see it's printing out the text empty onto our screen because there is nothing in our list. But if we add something to our list, you can now see the text full is being printed out. So we're able to conditionally return different HTML based on certain things in our page, which is really, really important. So here we have that empty array and I want to return essentially something saying hey you don't have any content. So let's create a paragraph tag that just says no habits yet. Add one above to get started. There we go.
And let's add some styles to this as well. So we can center the text. We can give it that zinc color we've been giving everything else. And we'll add quite a bit of padding on the top and bottom. Just like that. So now you can see no habits yet. Add one above to get started. So it just gives us a nice little placeholder text. Now, obviously, we want to do something if we have habits. So, let's pretend that we have a habit in our array and figure out what we want to do down here instead.
Well, I'm going to have essentially a flexbox based list. So, I'm going to put that in a class name with a flex of column and a gap of three. That's just going to space out our element. And then what I want to do is I want to loop through every element inside my array and print it out. So, if I have, for example, a bunch of different elements inside my array, I want to render every single one of them. Well, in order to do this in React is a little bit confusing because you can't just put a for loop right here and add like a return in the middle of it because that wouldn't work.
So instead, what you need to do is you need to take whatever array you have and you need to convert it into JSX that gets rendered out into your page. And the way that that works inside of React is you just type in whatever your array is and you want to map over it and you want to convert it to JSX output. So essentially you're converting it to HTML and then it renders that full list of HTML in your page. So we can loop through each one of our habits and then we can run whatever code we want to return from here.
Now you could come like this and type in that you want to return and we could say we want to return an H1 that puts whatever our habit text is inside of it. And now you can see for each element inside of our array it's rendering out the text for that element. If I remove an element you can see it now only renders two elements. Again if I have one element it renders it. And if I have no elements we are rendering this fallback value right here. So we at least are able to conditionally render stuff if we want to just like this.
And generally when you have this situation where you're just immediately returning JSX, what I like to do is instead of putting this like bracket right here, this curly bracket and return, I replace this with a parenthesy and a parenthesy on the other end. And essentially what that tells React is to automatically return the content in here. It's just a JavaScript feature. It just automatically returns what's in the parenthesis. So you can essentially save a little bit of code by writing like this. And it's most commonly what you'll see inside of React is code just like this.
Now, our habits are going to be a little bit more complicated than just a string. So, let's actually just get a little bit of a placeholder. For example, we're going to have an ID. Let's just say like an ID of one, and we're going to have a name of high. And let's copy that over cuz we're just going to create two for now. So, we'll have an ID of two on this one. And a name of by down here, we'll just render out that name property. So, there we go. You can see everything's still the same.
We have high by being rendered out. But, we actually have a problem with our code. Let's just inspect our page. I'll bring over the console real quick. And if we take a look at the console, you'll notice we get an error. Each child in a list should have a unique key property. This is something about React that's really confusing to beginners is that anytime you loop through an array, you actually need to put a unique identifier on the top element that you're returning. In our case, this H1. And this top element needs to have a key property that is unique to that particular element.
So in our case, each habit has an ID. So we can put the habit ID on there. Doing that won't change anything with your code. it all look exactly the same. But behind the scenes, React now knows which H1 is related to each element in your array. So, for example, if I remove element number two, it knows to remove just the second element and not change anything else on my page. So, it's really just there as a helper for performance. Or, for example, if I change my text to high two, it knows only ID1 is changing and that doesn't need to update anything else on my page.
Again, helping out with performance. So, anytime that you're looping through an array of elements and rendering them to the screen, always make sure you add a key property and make sure it's unique to that particular element. Usually, you'll have some type of ID, but in the absolute worst case scenario, you can use the index property just like this of the array. I would just highly recommend not using the index property unless you absolutely have to. Because, for example, if you delete element right here from your array, now your index properties are no longer matching up to the exact same elements.
So it can cause certain problems if your array content is changing, which is why I almost always recommend using some type of unique identifier, which you usually should have for each one of your elements. Now, obviously inside of our array, we want to render not just an H1, but we want to render out this fully complex component with lots of different things going on inside of it. This is a situation where you can easily create a completely separate file and a brand new component. Or in our case, what we're going to do is we're just going to create the component directly inside this file because it's kind of uniquely linked together.
This habit item we're going to be putting in here is only usable in this list. So, we're going to kind of have them both in the same file. But you could do this however you want. Separate file, same file, it doesn't matter. So, let's come in here. We'll call this habit item. And this is going to be each one of the items inside of our list is going to be what we render here. And if we just return an H1 that says hi. And down here, we replace this with our habit item. And we replace habit item.
Here, you can see we still need to pass along that key property because obviously we need to pass a key anytime we're using an array. And we don't need to worry about setting that as a property for our function because again, that's automatically handled by React. It is a name that is always there. Just like children is always available. Key is always available as a property. So now, if we give that a save and we look over here, it just renders out high twice because we're hard- coding that value of high. Now, in our case, realistically, we want to pass down each individual habit.
So we can pass down the entire object of habit to our property here. And again to make our code a little cleaner, we'll just make it self-closing. And now in here we can take in a habit property. And we'll call this our habit item props as our type. And let's create a type for that. So we have our habit item props. We know that it has a habit property. And this property is just equal to whatever we have up here. So we're going to create an ID. We'll make it a string instead of a number.
And we'll also come in here with a name and that's going to be a string as well. So let's make sure that these are string based ids. There we go. And now you can see everything is working. All it's saying is that we need to make sure we use this habit variable. But this means that we can pass along not just strings and numbers and booleans. We can pass along full objects and even functions to other components. You can literally pass anything you want to another component. So now here I could put the habit.name and it's going to now work just like it was before where it says hi and buy.
Now in our case, let's go ahead and style out what everything's looking like. And essentially we have two sections. So, we have this header section with all of our text and the delete button. And then we have our bottom section where we can like toggle what different days we've actually done this habit or not. So, let's break this down. We're going to use a div to separate our two sides. And we're going to have a class name of rounded XL background zinc 800 and a padding of four. Just like that. That's going to give us the space for our different content.
And then we're going to have our header section as well as our footer or the buttons. So our header section is going to be a class of flex. Items are going to be in the center justified between and we're going to put a margin bottom of three on the bottom of that section. And now if I just put some random text in here, you can see we have those sections and we have that little margin on the bottom. And instead of a margin bottom, I'm actually going to change this to a flex column with a gap of three just cuz I like using gaps rather than margins.
And it's going to give us the exact same result. But you can see it removed that extra stuff because there's no content after here. If we add another div, which is going to be our bottom. You can now see it properly spaces those elements out. So now let's go ahead and work on the rest of this header section. So we have the layout of it done. Next, we want the title. So the title is going to have a class here of font medium. So it's a slightly more bold font. And that's going to be our name.
So our habit.name just like that. Here you can see we have our names being shown up just like we want. Next, we're going to have that streak section showing up. So inside of here, we're going to come in with a span. Class name is going to be text small. We're going to use text amber of 400. Just like that. And then we can put whatever our streak is inside of here. So let's say it's a three long streak. And we'll just use a fire emoji. You could obviously use a real icon in place, but we're just putting a hard-coded emoji.
So you can see we get that nice little fire streak of three. Now, we obviously want these elements to be touching each other. So I'm actually going to wrap them in their own div just like this. And I'm going to make sure that they have a small space between each other of three and that they line up in the center. There we go. So now they are touching each other with a very small space between them. Next, we want to add that button for delete right over here. So we can come in here using our same exact button component.
And what we're going to do inside this button component is just put the text of delete. And we'll give that a save. And again, it's going to look different because we have a much different styled button, but we're going to be using a lot of the same exact things in our original button. So I'm going to be keeping this as is. And again, I'm going to come back to showing you how we can create this more custom version of the button by tweaking existing components, but it's a slightly more advanced feature. So, there is our button section completely done.
Now, all we need is this bottom section for all of the different dates we're going to be rendering. And that can go inside of its own div. And let's make sure we add a class name to this of flex with a gap of 1.5 to space out each button from one another. Now, in this section, we could try hard- coding seven dates like this. Or what we could do is we could just loop through an array of all the dates that we have. So let's just create a thing called visible dates. And that's going to be an array for each one of the dates we want to render.
And they're just going to be dates. So it'll be like new date and that's going to be what the current date is. Then we get the previous date and the previous date and so on. So we want to render out all those different elements inside this section. So what we can do is we can say visible dates map. I want to map through each one of our dates and I want to render out some content specifically for each one of those dates. Now, for each one of these dates, we can do that exact same trick with the parenthesis.
And we can return a button because again, we're using a button for almost every single thing in our application. We want to make sure we specify a key. In our case, the key is going to be our date converted to an ISO string that'll be unique for each individual date. And now, what we can do inside the button is render out our content we want. And we don't need to just render out text. The nice thing about passing along children is you can render out other JSX or in our case, HTML. So we can render out for example a span that's going to contain a class name of font medium.
And then we can come in here and we can format whatever we want for our particular day. So I'll just put two for now. And we can do the exact same thing down below. This is just a normal span, not bolded at all. And we can put out the text we want. So this is like Monday the 2nd for example. Now if we come back to our application, you can see it's rendering out Monday 2, Monday 2. That's cuz we have one date being rendered. If we had multiple dates inside of this section, just like that, you can see it's rendering all of them out and spacing them out from one another.
Now, obviously, we need to make sure that we format our button to look proper because right now it looks completely different than the button here. We also need to make sure we do all of our date handling properly. So, I'm going to be installing a library. This library is called date FNS. And this is just a library that makes working with dates so much easier inside of JavaScript. So, now what we can do is we can use a bunch of different helper functions to get all the code we want. For example, if I wanted to render out the current week, I could come in here and I could say start of week just like that.
Make sure that this gets imported properly from the library from date FNS. And this is start of week. This is going to give me whatever day is the start of our week. So I can say start of week like this. That's going to give me the very first day of the week. And then we make sure we pass in the week we want to handle. So I pass it in a date and it's going to return to me a date that is the current start of that week. I can also get the end of the week and I can get all the days inside of an interval.
So I can say each day of interval just like this. I can pass it in a start location which is the start of the week. I can pass it in an end location which is the end of the week just like that. And now essentially what this is going to do if I close off all my parenthesis properly is it's going to give me a full array that is going to start at the start of the week and it's going to end at the end of the week. and it gives me every single day in between them.
So now if I give that a quick save and I make sure I restart our application, we should hopefully see over here we now have 1 2 3 4 5 6 seven different days being rendered. Now let's make sure we render our text instead of hard coding this. And for that we can use the format function which comes from date FNS. This function is really nice. You pass it in a date. So each one of our individual dates. Then you can pass it a bunch of different format strings. You can look up all of them if you want.
But if you type in three e, that's going to give you the day of the week as a nice little threeletter abbreviation. We can do the exact same thing down here. And if we want to get the day of the month, we can just pass in a single d. And that's going to give us the day of the month. So we have 5 6 7 8 9 10 11. Now in our case, I want the week to start on a different day. So we can say here that we want to have the week start on a 1.
And that's going to start the week on a Monday instead of a Sunday. So I can do that for both. And now you can see our week starts on Monday and ends on Sunday. So, we get that nice full 7-day list right here. Now, let's go up to our habit list. We're going to remove all but one of these elements so we just have our very first one so it's easier for us to take a look at. And now, what I want to do is I want to do what I've been talking about this whole time is making it so we can customize the code being passed along to our button so we can get all of our different styles for our element to look exactly as we want to.
For example, I want to disable some of these buttons that are in the future. And I also want to make sure that my styles are proper. So, let's first take a look at how we can pass along disabled. So, I can come in here. I can say disabled just like that. in my button. I could take in the disabled property which is a boolean. So I can get my disabled property and I can pass it along directly to my button. So essentially I'm just forwarding this disabled property onto my button just like this. Passing it to my button here, passing it to my normal button.
And now if I come into my section and I make sure this is an optional property that defaults to false. That'll clean up all my errors. Now if I come into here, I can pass disabled. And you can see all my buttons are disabled because I passed along disabled. And I could even make it so it only disables the future ones. So I can say is future and I can pass it in my date just like that. And now all the dates that are in the future, for example, Friday, Saturday, Sunday since I'm recording this on a Thursday, are going to show up grayed out.
While all the dates that are current or in the past are going to show up not disabled. But this is kind of cumbersome, especially if I want to pass along a bunch of different properties. Because if we take a look at button and we just hit control space, these are all the properties available to a button. You can see there's just a few of them. So, if I wanted to have to manually pass every single one of those by passing the prop here, putting the prop up here, and then putting it down here, that is a massive pain to deal with.
This is why inside of React, they actually give you a little helper that lets you access every single property that is available on an element. So, what we can do is we can add on to our existing button properties another set of properties. And this is going to be called component props. And inside of React, this is a generic where you can pass it along any component. It can be a custom component or it can be a string of any existing HTML element in our case button. And what it's going to do is it's going to essentially give you all of those properties as accessible from this element.
So here if I just come in and I get disabled, you can see it's there. And you can see every other HTML property I could add to a button is now available in this section. I'm going to group all those into one element just called props. So I can say props just like that. So now I have a single variable that includes every single property that's available on our button element and I can just pass those down just like this. So by wrapping it in the curly braces that's passing down JavaScript. And if I just put dot dot dot followed by this giant object, it's going to take every single property in that object.
And essentially it's going to do this. It's going to say disabled is equal to props. And it's going to go through and do that for every single property in this object. So for every single property I can pass down, it sets every single one of them. That's what this code right here is doing. And since by default buttons have a children property already defined on them, that is actually included in this props right here. So I don't even need to pass along my children at all. I can just close this off. And it's automatically going to pass down the children for me by just saying children is equal to props.
Just like that, which is going to set the children in my element just the same as if I passed it along inside of the body of my element. And you can see everything still works exactly the same when I did this specific code right here. And I don't even need this children section right there at all because again it's automatically handled for me by the React component props. And now here I can pass along any custom properties I want to. In our case, I'm going to create a property called variant. And this variant is going to be how I determine what my button looks like.
Am I going to have a primary button, secondary button, and so on. So, I can come in here with a button that's going to be primary or it's going to be secondary or I'm going to have what's called a ghost destructive, and that's going to be representing our delete button. If we look at the delete button, you can see it's this like red button that when we hover it, it gives us the red background. That's what ghost destructive is. Secondary is going to be these gray buttons. And then primary is going to be any of our purple buttons.
So, we can fine-tune what our colors look like based on these variant properties we pass in. So, now I have this variant property. I can do whatever else I want with. Now, by default, I'm going to make this variant property optional, and it's going to be set equal to primary as the main value for it. And now, I want to create a simple function that is going to give me my custom CSS styles. So, let's call this get variant styles. Just like that. This is going to take in my variant, which let's create a custom type for.
And let's just copy all that over to there. Perfect. So, it's going to take in whatever my variant is. And then I want to do a simple switch statement on that variant. And the reason I like to do a switch is because at the bottom I can put a default case. And this default case is just going to throw a new error. And it's going to say invalid variant. And I'm going to put in my variant just like that. And the nice thing is I can say satisfies never. And if I'm using TypeScript, this will give me an error unless I handle every single case in my variant here.
And if you want to learn more about this satisfies keyword and everything related to it, I'll link a video in the cards and description going over in depth why I love this property. So now I can come in here with each case. I need to make sure I handle them all. So we'll start with primary and I want to return some CSS styles. So we're going to say background is going to be essentially our default background and hover. So I'm just going to copy that, paste it into here. Next, we're going to do the exact same thing for our other two cases.
So we have our secondary. Our secondary case is going to be a gray color. So we'll say zinc of 700 is going to be what we want our background to be. We want our hover background for this one to also be zinc. So we'll say it's going to be a zinc of 600. And then we also want to change our text to be a zinc of 400. So it changes everything related to that. And then finally we want our ghost destructive. And this is going to be a red color. So we're going to say background red.
And this is only when we hover. We want our background red to be 800. So a nice bright or dark red background color. We also want to make sure that our text is a red 800 by default. And then finally, when we hover, we want our text to be a red of 200. So it's going to be a very bright red while this is a dark red color. Now, the nice thing is we can take this get variant styles function and put it directly into our class names. So here again, if you want to run JavaScript, pass it inside of curly braces like this.
And then we want to use string interpolation. So we're going to use these back ticks. And then we can pass along our function and our variant directly into it like this. And there we go. We've now added all of those class names directly to the start of this list right here. So now if we take a look at our buttons, they all still look exactly the same because they all have a default variant of primary. But if we wanted to change some of these, for example, in my habit items. So let's go into our list here and our delete button which is inside this section right here.
Let's say I want to change the variant on this and I want to change it to that ghost destructive. Just like that. Now, if I give it a save, you can see it gives us all those red styles because essentially that's what we're doing in our button is we're getting these red styles when we pass in a specific variant. This is a very common thing that you're going to do inside of React is essentially add in variants to your components. So, you can use one component that shares a lot of the same stuff, but you can do slightly different stylings or different features for it based on these different variants and so on.
Now, another thing that I want to be able to do is pass along custom class names to my buttons. So, for example, this add habit button I can make slightly bigger or these buttons I can make stack vertically instead. And since my component props for buttons already has a class name, I can actually just take that in like this. It's already available. And now I can make sure I pass along my custom class names to this pre-existing class name section as well by just putting them at the end like that. So now I can add on my own custom class names as well as these existing variants right here.
So let's go back into our habit list and actually let's just go into our habit form because this one's going to be relatively easy to add our different classes to. I can come in here with class names and for example I could change the background to red 500 and if I save you'll notice it doesn't quite look like it's working. The reason that it's not working is because I have this background red 500 and I'm also inside my button applying a background violet 600 and tailwind doesn't have like any specificity or anything. It just defines whichever one is last in the CSS and violet happens to be after red.
So that's why it's not showing up as a red color. Instead I need to use a custom library called Tailwind merge. This is only useful if you're using Tailwind, but since a lot of people use Tailwind in React, that's why I'm using it. And if you're using Tailwind, you essentially need this library. So, let's go ahead and install this library. And I'll show you why it's so useful. It's called Tailwind- Just like that. And now, what we can do is we can merge together properties, and essentially, it's going to take whatever is the last property we passed to it and use that one instead.
So, here for our class names, we can use that Tailwind merge function. That's the only thing that comes from this library that we're importing, the only thing we care about at least. And what we need to do is we need to pass it our default styles that we want. So we're going to pass it our variant styles. And we're going to pass it all of these other custom styles as well. Just like that. So the very first property is our variant styles. Second property is all of our custom styles here. And then our third property is going to be our custom class names we want to overwrite everything with.
Just like that. We give it a save. You can see first thing is this, second thing is this, and third thing is this. Technically, it should probably be in this order. And now essentially what's going to happen is it's going to take all of these styles and apply them. Then it's going to look in this variant styles and if any of the styles in here are clashing with the styles in this first section, it'll take the ones in the second and overwrite the ones in the first. It'll then do the exact same thing with the class name here, it'll take any of these classes and overwrite anything that was defined before it.
So our red color should be defined last. So it should be overwriting all the rest of our colors. So let's go ahead and test that. Let's actually just restart our application. We should actually immediately see that now our button is that red color. So it is overwriting everything just like I expected it to. And no matter what we do or order we pass things in, it'll always use these class names to overwrite anything that existed before. Now in our case, I don't want to change much. I just want to make it so this is a slightly bigger button.
So I'm going to give it a little bit of extra rounding by saying rounding of large. I'm going to give it a little bit of extra padding. And then I'll just make it so the font is bold. And actually we'll use medium for that. So you can see we just made a slightly bigger version of our button, but otherwise it works exactly the same. And we're able to do that by essentially using that Tailwind merge to merge everything together. Now, if you're not using Tailwind, you don't need that Tailwind merge, but a similar concept applies.
Now, let's go ahead and work on the rest of our items in this habit list because once we get that done, we can start working on the more interesting part, which is adding functionality to our application. So, inside this visible date section, we want to add a bunch of classes to our button to make it work like we want. For example, we want it to be flexbox based. So we'll come in here with a flex, a flex of one, and a flex column. By adding flex one, it just makes it span the full width. So for example, if we remove that flex one, you can see they don't span the full width.
Flex one forces them all to span the full width. Then we have a flex column so that it's going to be vertically stacking our items. We want to also put our items in the center. And we want to have a very, very tiny gap of 0.5 between our items. We'll make our buttons slightly more rounded. And we'll change the text to be quite small inside the buttons. And you can see already that's given us pretty much exactly what we want compared to what we have over here. Everything looks essentially exactly the same. The only minor difference is that our button up here for the delete.
We'll add a custom class name to make the text extra small. And that should be exactly the same now between both of these different sections. And actually, I think small text is probably enough. And now you can see these are exactly the same as the other one. So we've got the entire styling of our application done. Pretty much the only thing that we have to do next is to actually make sure we get the functionality working which is the much more enjoyable part. Now the first thing to understand about functionality in React is that everything runs through state essentially.
So if we go into our form this is going to be the easiest place for us to add a different piece of state because we need to be able to track what this input is and then use that data when we click on this add habit button. So let's go ahead and we'll keep a piece of state to track what the value of our input is. So we can create a state variable by using the use state hook in React. This use state hook takes a default value. We'll pass it an empty string as our default value.
Anytime you use inputs, always make sure you pass an empty string as the default value. And this use state hook actually returns to us two values in the form of an array. It's going to return us the actual value of our state, which we'll just call name. And then it's going to return to you a function you can use to update the value of your state. So we can say set name, and that's going to be the function we call to update what this state variable is. So now we have our state variable and a way to update that state variable.
So inside of our input, what we can do is we can say that our value which is the value of our input. We can set that equal to our name property. And now we've essentially hooked up half of our state. We have hard-coded the input value to always be equal to whatever our name is. But now I can't actually type in this input because my name value is hardcoded as the value and it's always an empty string. We're never updating our name. In order to update our name, we can come in here with the onchange event listener.
that'll fire every time we change our input. And we can take the event and we can call set name pass it along our e.target value. This is just whatever the current value of our input is. So now we have this state variable called name that is being updated every single time we type in our input and then it's putting that value back in our input. Now if we type everything works exactly the same as it did before. But the reason this is really useful is because now we have this name variable and we can use it anywhere in our application.
So let's just come in here and just randomly place down our name variable just like that. So now if I start typing, you can see that name variable is automatically updated every single time I change it anywhere I use it in my application. I'm just using it in one place, but it's being automatically updated. And that's the important thing to understand about React is state is the one thing that causes your application to rerender. So the way React works is kind of different than like JavaScript and that it's a reactive declarative language. So we declare what our UI output should look like and then every single time something changes in our state, it rerenders everything and reacts to those changes.
So essentially what's happening is the first time you load this page, it runs all the code inside these statements for return and returns exactly what there is. And then it stays like that forever on your page until your state changes. Anytime you change state, it takes all of your components that are within this state section. So all of this code right here, it reruns all of that code with the brand new state variable and then updates everything in your UI. So when our name changes, it reruns all the code inside of here. It says, "Okay, my new name variable is this, places that in the HTML, and then reshows that to the user." That's how everything in React is going to work for you.
So, it's important to understand how state works because it is the one way you cause your application to rerender and make it actually reactive, which is what makes working in React so enjoyable compared to something like JavaScript where you have to manually keep track of what needs to change every time a variable updates. React is just automatically smart enough to handle all of that rerendering for you, which is what I really love about it and what other people love about it as well. So, now we have the ability to add this name variable, update it, but we aren't doing anything with it.
We want to make sure we add a new habit with that particular name variable. So inside of our form, we can just create an onsubmit event listener. We can call this whatever we want. I'll call it handle submit. We'll create a function for that. Handle submit. We know this takes in an event and this is going to be a form event. Make sure we get the correct form event. And actually it's been changed to submit event. There we go. So that's going to be the event for our form. And we can just say e.prevent default.
Just like that. And we are getting an error. And that's because I need to make sure I get the submit event specifically from React. When I import the React version, that's going to clean up all of our types. And by preventing the default, it just makes it so that when I click submit, it doesn't automatically refresh my form. If I actually remove that and I click on add habit, it's going to refresh my whole page and try to do a default form submission, which is not what I want cuz I want to handle this myself.
Now I can come in here and just console log our name. So now I can type in a name here, click add habit, and if I look at our actual page, go into the console, you can see that is being printed out right here as my default value. And that's great, but I want to be able to do something with this value as well. Let's first before we handle that, deal with the rest of our application. Because here I can make this disabled. If my name is equal to an empty string. So if I say name.trim is equal to an empty string, well then I want to disable this component completely.
So let's come in here just like that. So now if I don't have any content, it shows that button right there. And I can just say if my name.trim trim is equal to an empty string then I can just return as well just so I don't actually do anything if I have no data to submit also after I submit my form by clicking this button I want to clear this input so I can just say set name set it to an empty string and now when I click submit it'll clear out my input I can type something new click submit clears it out and that works just like we expect now in order to use this name property to add a new habit this is where we actually get to some of the more confusing parts of React because I want to add a new habit inside this habit form is where I want to add it.
But I'm using those habits in other parts of my application. For example, I'm using them in this habit list right here. There's no way to talk side to side to different components. I need to only be able to talk to components that are children of mine or be able to call functions that come down from the parent. So essentially to store a state that can be used in both the habit item and inside the habit form, I need to store that in a place that has access to both of those components. And in our case, that is the app component.
It's the only place that has access to our form and our list. Essentially, you just need to go up high enough that you're at a level that has access to both of the children component. Even if they're deeply nested, that doesn't matter. So, now here is where I want to store my list of all of my different habits. So, I want to have my habits here, and I want to be able to add a new habit in my form and pass down my habits to my list down here. Because right now, my list has my habits hard-coded.
I actually want to get rid of that and make it so that my habits are passed into this component. So we can say that we're going to get the props habit list props. And let's type that out. And here we just have habits which is that type of habit. And we want to get an array. And I don't think I typed out my habit. So I'm going to create a type for that. There we go. And down here, let's just make this a type of habit. That way, all of our types line up. So, now we can pass our habits into our habit list.
So, right here, I can pass in my habits just like that. And right now, they're just a default empty array with no data inside them. Now, in React, if you want your application to react to changes, for example, changes to this habit variable, we need to use state. So, I'm going to replace this with a call to use state. And by default, we'll set it to an empty array. And then, like I said, this returns two objects. the ability to set our habits and the ability right here to update or to get our state value.
Now, right now, this is just going to be an empty array of never values. We need to be able to type what this value should be and we know that it should be a habit array just like that. So, we need to get that habit type and export it. So, let's just go into here and we'll export the type for our habit and we will import that right here. So, now we know that this must be an array of different habit values and everything is working just fine. But now in my habit form, I want a way to add a new habit from here.
Essentially, I just want to call a function add habit, pass it in a name, and have everything work. Well, we can actually do that. This function can be passed down into this component. So, we can have this add habit function and we can pass it down. Let's just make sure we type this out. This is going to be a function that takes in a name, which is a string, and it's going to return nothing. So, void. Now we have a function that we can use to actually add a habit to our page and we can pass that down from our app right here by just saying add habit and let's just create a function with that name.
So we can pass it down. There we go. This is going to take in a name which is a string and then we can do whatever we want with that string. For now let's just console.log the name. So, this looks like I've done a lot of convoluted stuff, but really all I've done is I've taken this habits code that used to be hard-coded in our list and used to be inaccessible from our form. I put it in a location that is shared by both of these elements. So, now I can pass the habits down here and I can pass down a way to update it into our habit form and that allows me to update our data from this location and to be able to use it inside of this habit list location.
Essentially, any time in React that you want to be able to use components or state, you need to be able to put that in a location that's accessible by both those components that are trying to use it. And if you want to update state that is inside of a parent, for example, this habit state is in our app component, which is a parent to our form. Anytime you want to update state, you must pass down a function that allows you to do that state update. So, in this add habit function, this is where we can update our state.
So for example, I could say set habits and I can pass in whatever my habits is going to look like inside this component. Now a very very important thing to understand about a react is that state variables are immutable. That means you cannot change this habits variable except for by calling this function. So if I were to do habits.push and I push in my name like this, you would think, well that'll add my name to the list. I just need to make sure I give it an ID. So we'll say crypto.random UU ID and the name will be set to our name.
That adds a brand new habit to my array. And you'd think, well, that's all I need to do. That should work just fine. But if I type in here, add a new habit, and click enter, nothing actually happens. And that's because, like I said, mutating state does nothing. React does not care if you change the state. It completely ignores it. The only way to get it to update is by calling set habits. So you may think, okay, well, I'll add the new array, and then we'll pass it into here. Just like that. Now everything should work fine, right?
I type in my value, click enter, and again, nothing actually happens. And the reason for this is because the habits array is the exact same reference to the previous array that it was before. The way that state happens for checking if there should be an update or not is it takes your old state value. So we'll say old and it compares it using triple equals with the new value. So essentially what's happening is it's saying habits is equal to habits. These are both the exact same array. Even though they have different values, they reference the same location.
So they're both the exact same array. So this is returning true. So there's no state to update because it's still the same. Instead, what you need to do is you need to return a brand new array inside of this section right here. So what we can do is we can take all of our habits and then we can add a brand new habit to the very end of this. Now what this is going to do is it's going to make sure that it updates our habit array with a brand new habit at the end and it's always returning a new array.
So React sees this as a new array and rerenders the component accordingly. So let's just come in here. I'll add something. 1 2 3 and you can see every time I'm adding something, it's adding it to the end of my array, which is exactly what I want. Now, let's refresh just to get us back to a starting point with just one element. And I want to talk about a potential bug that we have inside of our code. Anytime that you're using the existing value of state inside your state updater, you need to make sure that you're actually changing how your code works by referencing the second version of this set habits function.
Let me show you the potential bug we're running into. Let's say that I want to add my array or my habit to the end of the array twice. So I call set habits once adding it and then I call it again adding it a second time. So I would think this would add my element to the array twice. But in reality it only adds it once. You can see if I refresh my page, type one, it only adds it one time, not twice. This is because this habits value right here is always the same. The page doesn't rerender until after all of our set state changes have been made.
So, it takes all these changes, batches them all together, and since our habits value is the same between all of them, it only adds it one single time. What we need to do is actually take in the previous value or the current value of our thing. So, we'll call this current, just like this. And this is going to take in a function that's then going to return our new state value. So, we can take our current value, and we can pass it along our new value, which is that crypto.random UyUid, and our name, just like…
Transcript truncated. Watch the full video for the complete content.
More from Web Dev Simplified
Get daily recaps from
Web Dev Simplified
AI-powered summaries delivered to your inbox. Save hours every week while staying fully informed.






![[LIVE] TANNER LINSLEY: TanStack Start, React, AI Agents, and More thumbnail](https://rewiz.app/images?url=https://i.ytimg.com/vi/AQOPaHHYQFk/maxresdefault.jpg)


