React Notes App Tutorial from Scratch | A CSS and React Project you can add to your Portfolio!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
if you're looking for a fun project to build that you can actually use we will be doing just that in this video we will build this app from scratch writing our own css and react components we will add some cool features like adding a character count when the user types we will add a search bar that lets the user search for a note we will add dark mode which you can toggle on and off and even save to local storage so that you can use this app in real life you can find the links to the style sheet and the finished code in the description i've also included timestamps in the description so you can skip or jump to different parts as you wish let's get started we'll start by creating a new react project the simplest way to do this is to use create react app this is a tool that lets us create a skeleton app without having to set up all the config and infrastructure files ourselves you can explore this more by going to the github check the links in the description for this okay so we'll open up visual studio code and we'll open a new terminal by going to terminal new terminal this should open for us at the bottom and here we want to change our directory to where we want our project to be so in this case i'm going to put the project on the desktop next we'll type mpx create react app and we'll give it a name in this case we'll call it react notes app next we will install react icons which is a package that gives us some nice icons to play with go ahead and change your directory to the new project by typing cd react notes up and type npm install react icons next we'll open the project in the editor so if you click the open folder button select the folder that was created for us by create react app and click open this will open our project in the editor as you can see the files have appeared in the project tree so we're going to go ahead and delete some stuff that we don't need so delete app.css app.test.js we'll delete the logo report web vitals and setup tests.js next we'll jump into index.js and we can delete the import for report web vitals and we can delete this stuff at the bottom finally we'll jump into app.js and we'll delete everything here and we'll create our own app component from scratch so we'll say const app is equal to an arrow function and this will just return some text which we will display on the browser just to make sure everything is working and we'll export this component at the bottom like so now if we open up our terminal again and this time we'll do mpn start this will open up a browser with our text hello react and if we change some things in our code and see if it should automatically update and it does so that's our project set up and ready to go okay so we're going to jump straight into the good stuff and create our notes list component if we look at the finished example this is what the notes list will look like so we're going to have a few notes in this grid layout and we're going to have a thing down here that lets us add some notes as well we'll add the title and the birchbar thing later but for now we're just going to concentrate on this list back in our code if you go to the source folder and create a new folder called components and here we will create a file called notes list dot js and we'll create our function so it'll be const notes list it's going to be equal to an arrow function in the return statement of our function we'll add a div of a class name of notes list like so and we'll export our function within the notes list will take a few lines and we'll render a note component which we haven't created yet so we'll likely have more notes in here at some point placing the notes inside a div like this makes it easier first to control the layout of the notes using css grid which we'll see shortly so we have our notes list which holds all the notes next we'll create a note component which is the yellow box that we see here so back in our code if we jump back into the components folder add a new file called note.js and we'll create our function const note it's going to be equal to an arrow function and in the return statement we're going to have a div a class name of note like so and we'll export our component so the note div is going to act as the parent div for this note component this makes it easier for us to style an individual note and easier to position the individual elements within a note let's add the different parts of the note before we get to the styling if we look at our finished example we're going to have a section that holds the text up here and we're going to have a section at the bottom which holds the date that the note was written on and a delete icon back in our code we'll take a few lines inside the div we'll add a span this will hold the text of the note you can put whatever you want in here i'm just going to say hello this is our first note like so next we'll add a div for the footer which will give a class name of note footer this is going to hold the date and the delete icon so we'll add a small tag and put our date inside it this can be anything you want for now and next we need to import the delete icon from react icons so at the top of our file we'll do import open braces md delete forever this is just the name of the icon from the package from react dash icons slash md just like so and to render this icon we jump back into our jsx just below the small tag open an angle brace and we'll type md delete forever and close our braces like so and we will give this a class name of delete icon since we're going to add some styles to this later we'll give it a size of 1.3 em like so this is the basic structure of our note now if we jump back into the notes list component we need to import the note as it's telling us that note is not defined so at the top hold the import note from open quotes dot slash note like so and our error goes away next we'll import our notes list into our app so if we jump into app.js we can delete this paragraph tag and we'll add a div with the class name of container so this is just going to be the div that helps us make the note app responsive and inside our container we will render the notes list like so and if visual studio code doesn't import the component for you just make sure that you add import nodes list from component slash notes list at the top now if we save this and start the app if you haven't already and we'll jump into the browser and yep it looks pretty terrible that's okay since we're just putting our blocks in place and we haven't added any styles yet we'll jump back into the code and open up and we'll just add a few more notes in here as this makes things a bit easier to see visually when we come to position everything so if we save this and we'll jump back into our browser so we have our four notes here and they all appear very ugly if we look in the dev tools you can see we have the basic structure of our app so we have a container we have the notes list inside the notes list we've got one two three four notes and inside each note we've got our text and we have our footer section inside the fitter section we have our date and we've got our icon okay so now we've got some elements in place we'll look at styling each note and position things using css grid okay so we'll start by styling our note remember we've added a div with a class name of note in the note component which keeps all the related note elements together so the note class is the first one we'll work with let's open up index.css and as you can see create react app has given us some very basic styles already so we'll just leave these alone as we don't need them and they don't do very much if you prefer to copy the style sheet instead of typing it to css you can get the link to the github file in the description so in here we will take a new line at the bottom and we're just going to type dot note and open some braces so all these styles will get applied to any element with the note class the first thing we'll do is add a background color and this is going to be a value of f e f 6 8 a we don't want it to be too yellow as this can be quite jarring but this can be any color you like really and if we see it you can see our app updates in the browser so we'll just keep this open so we can visualize the code changes as we go next we'll add a margin bottom so say margin bottom and we'll give this a value of 10 px this is just a temporary style to space out the notes so we can see each one better later we'll remove this and use css grid to handle the spacing as this makes for better responsiveness as well so next we'll add the border radius and we'll give this a value of 10. this makes things look less sharp and easier on the eye doing this has actually pushed our text a bit too close to the edge of our note as you can see over here so we're going to add some padding which will give us a gap around the inner border of the note and push the text away so we'll say padding and we'll just give this a value of 1 rem so rem is a unit which means relative to the font size if we change the font size then the padding will change size as well and it means you don't have to go and change this lastly we'll add a min height as we want all our notes to be the same size so we'll just hard code a value of 170 pixels and as you can see it's updated our height okay so our notes are coming along well now we want to position the dit and the remove icon so that they sit at the bottom of the note so if we look at our finished example we have the date to the bottom left and we have our remove icon to the bottom right when we want to position things horizontally or vertically in a single line we can achieve this by using flexbox we will say display flex to say the note div as a flexbox container and if we save this as you can see in the browser the default thing that flexbox does is arrange the child elements in a single row so we have our text off to the left and the note fitter element is off to the right so to fix this we will say flex direction column and if we save you can see things are now stacked vertically next we will say justify content space between which means evenly space the items within the note container with the first item positioned at the start of the container and the last item positioned at the end of the container so if we save this you can see things have dropped to the bottom and our text has been pushed to the top so because we have set the flex direction has been a column the start of the container in this case is going to be the top of the note and the end of the container is going to be the bottom of the note flexbox works by positioning its direct children if we look at note.js we've only got two child elements the span and the fitter div these are the things that blexbox will position flexbox doesn't care that there's text inside the span or that there are more elements inside the note fitter it only cares about its direct children so we have our note text and our note footer nicely spaced out although our footer is looking a bit funny there's no spacing and the alignment is a bit off so we'll look into that now if we look at note js you'll see we put the date and the delete icon inside a div with a class name of note footer this note footer div will be the flexbox container allowing us to position the date and the delete icon a bit easier so we'll jump into index.css and create a new class just below the note class called note footer we will add display flex to say that this is a flexbox container and next we'll add align items center so this vertically aligns the items to be in the center of the container currently things are a bit off as we can see over here but if we save this watch what happens everything now aligns up nicely next we'll add justified content space between so that the first child element gets positioned at the start of the container and the last child element will get positioned at the end of the container over here so if we save this it works now if we move the window about you can see that no matter what size we go to the elements always stay where they're supposed to be however if we expand the window too much you'll notice that our notes become massive this is because they are plain old div elements still so we'll look at how to use css grid to help us arrange these notes nicely okay so now we're going to use css grid to position and resize the notes to make everything responsive if we look at noteslist.js you can see we have wrapped the notes in a div with a class name of notes list this div will be the css grid container and it works similar to flexbox in that we can specify how we want the layout to work and css group will arrange its direct children based on this so if we jump back into index.css we'll remove the margin bottom from the note class since we're using css grid for the layout now we will create a new class called notes list and the first property that we will add will be display grid so this just says that the notes list div is a css grid container next we'll add a grid gap of one rem this adds spacing around each of our grid items in this case our notes so if we save this you can see it's added some spacing just below each one next we'll add the grid template columns this specifies how many columns we want our grid to have we have four notes for now so we'll say repeat four 4 inside the braces comma 250 pixels this tells css grid okay i want four columns each one with a set width of 250 pixels if we save this we can see it kind of works as our notes are now being resized correctly and are on the same line but as you can see it's not responsive the problem here is that we're hard coding the number of columns to 4 and the size of the columns to 250. we'll delete this 4 and we will say auto fit this will automatically resize our columns based on our column size which we specified here so instead of the 250 we're going to delete this we're going to type min max and open some braces in here we'll type 250 px comma 1 fr this lets us specify a range as opposed to a single value a column cannot be smaller than 250 pixels and it cannot be bigger than one fractional unit a fractional unit just means whatever space is available now if we save this watch what happens in the browser everything suddenly starts to work so if we make our browser bigger you can see the notes are resizing and when css grid detects that another note of 250 pixels can fit on the screen in the next column it automatically adds it for us and same if we go back into the small size it only has one on each line this line is doing most of the magic and it basically means fit as many columns as you can in a row and each column can resize within the given range to fit whatever space is available so it's a very clever line of code so it's good to understand what's happening feel free to stop here and play around with this a bit more and explore how it works if you want me to go more in depth with css grid drop me a comment below okay so now we've made our actual notes list responsive we'll add a container div to the app to help us center everything in the middle of the page on larger screens this is good for ux as it means the user isn't having to look at the full width of the screen just above the notes list we'll add a class called container so in here we'll add a max width of 960 pixels this means the stuff within the container will not go over this width next we'll add margin right of auto and margin left auto this helps keep everything centered on large screens so as we expand out you can see the notes stay in the middle of the screen regardless of the screen size finally we'll add some padding so we'll type padding right of 15 pixels and paddling left of 15 pixels as well this prevents the notes from being pushed to the edge of the screen so if we see this you can see it just adds a bit of padding to the left and the right which looks nicer so expand it again and you can see our notes list is quite responsive okay so now our notes list is nice and responsive we will render the list based on some dynamic data before we can do this we first need a list of notes in app.js we'll use the use date hook to hold our array of notes so just inside the app function we'll take a new line we'll type const open our square bases we'll type notes comma set nodes and this is going to be equal to use state and we need to import the state took from react so we'll import you state from react since we're going to be actively changing this data it's a good idea to put it in state and because we are working with lists we will store our notes in an array we can initialize a state hook by passing in a value to the use state function this is a good place to pass our array each item in the array is going to hold certain things we need to know about a given note if we look at the finished example we need to know the text of the note and we need to know the date so we'll create a few objects with some domain note data inside our array we'll open our braces and we will add the text so this can be whatever you want i'm just going to add some text in here that says this is my first note and we'll do a comma and we'll add the date so the date is going to be in the short string format so for this one i'll just say 15 04 slash 2021 we'll also add an id in here since we'll need this later when deleting notes so to create an id we're just going to use nano id which we call like so we just need to import this so at the top we will do import open braces nano id from nano id and save next we're going to copy this object a few times just to give us some more notes to play with and we'll change some of these things about so i'll change the text of the second one to say this is my second note and the text of the third note to say this is my third note and we'll change the dates just so that they're different okay so now we have our notes list stored in state we need to pass the notes variable to the notes list component down in here so that the notes less component can render each note we can do this via props so in our notes left component we'll create a new prop called notes and this is going to be equal to the notes we just created in state up here now in noteslist.js we destructure the props to get the notes that were just passed in so open our braces and we'll type notes next in our return function we will use the map function to loop over our list and render a note component so instead of a set number of notes this makes things more dynamic so just inside the notes list div we will take a new line open our braces and then we'll say loops.map and we'll pass in our function to the map function the map function will pass the current note to our function and then we want to say for each note we want to render a note component like so and we can delete these hard-coded notes now if we save this you can see in the browser it is displaying the three notes but the data is still showing the hard-coded data that we've added in the note component to defects this back in our map function for each node that gets rendered we want to pass in the text the date and the id as props to the node component so in our node component we'll create these props we'll say id is equal to note dot id text will be equal to note dot text and it will be note dot dit now in our note component we can destructure the props to get the things we need so we'll get the id the text and the date that was just passed in from the notes list to this note component now that we have this data we can go and delete the hard coded text and replace it with the text that was passed in by our props and we can do the same with the date so we don't need the id right now so we'll just leave that one alone now if we save and try this you can see our notes update from the array instead make this a bit smaller so we can see our code again and if we add another note uh in state so if we copy and paste this and we'll say this is my new note and we'll change the date as well if we save this you can see it has appeared at the bottom so i should mention that for this app we will store most of the state in the top level app component this makes sense to do as we will have different pieces of state that rely on each other and having this date in one place makes this easier okay so now we will look at how to add a new note if we look at the finished app we have this blue input that lets us type a note we also have this counter thing which tells us how many characters are remaining if we click save the note gets added to the list and the input resets the date gets automatically added based on the current date and a remove icon gets added as well we will start this one by adding the component and adding the styles then we'll add the javascript after in visual studio code if we open up our project tree go to source components and add a new file called add node.js in here we will create our component so say const add note it's going to be go to an arrow function and we'll open our braces and type return and open some braces in the return statement we will add a new div this will be the container div for the add note component which we will use to style the background and position the different elements the component is going to look similar to the note component so we can reuse the note class here instead of creating new styles go ahead and add class name equals note we will also add a new class so we can add specific styles to this add node component inside this div we're going to add a text area this is where the user can type their note we will add it rows so that it doesn't go too tall and add 10 columns so that it doesn't go too white lastly we'll add a placeholder that says type to add a note next we'll add the note fitter the note fitter is going to contain the save button and it's going to contain the characters remaining just outside the text area we will create a new div with a class name of node footer this will be the container div for the footer remember we already created a css class that positions the fitter elements for us when we created our node component so here we are simply reusing this class inside this div we'll add a small tag which will hold the character count you can just hard code some text for now just to make sure that things are visually okay and we will add the javascript for this later next we'll add our button [Applause] so the button is what triggers the event that saves the note we will give it a class of save and we'll add some text which says save as well lastly we'll export our component export default add note okay so this looks to be our ui elements in place let's see him and see what it looks like so if we save jump into our app and uh it hasn't appeared so let's have a look uh oh we haven't rendered our add node component in our notes list component so if we open up nodeslist.js we'll import the add node component so import add note from open quotes dot slash add note and just outside the map function in our jsx we will render the component now if we save this and hopefully it appears and it does so the reason we put it here is because we want it to be part of the css grid so that it flows nicely along with the other notes regardless of how many notes there are so it's showing up in our app now and it doesn't look too bad things are generally where we want them which is good now we just need to style it up so that it looks like the finished example we'll jump into index.cs and we'll take a new line under the notes list we want to keep the majority of the styles we created already when we created a note so we will add a new class called dot note dot new and open braces the styles we put in here will apply to all elements which have the note css class and the new css class so in our add note dot js we've added both these classes which means any styles that we add in index css will apply to this div this is a good approach to reusing styles without having to duplicate them so in here we'll say our background color and we'll just add a value so this will give us the bluish color but this can be whatever you want if we save this you can see down here that it's changed okay now we want to style the text area to remove the border and change the background color to match our component so go ahead and take a new line and then we'll say text area we don't need a dot in front of the class this time as this is a native html element that we are referencing open up the braces and we're going to say border none which removes the border and we'll say resize none so this removes the expandable thing which the text error comes with so we don't want the user to be able to drag this thing about and mess up our nice responsive layout so we'll just disable that and lastly we want the background color to match the components color so just copy the background color which we added up here and paste it down here now if you see this you can see it gives us this nice fancy input effect however if we click on it you can see it still has this blue border which doesn't look very nice so to remove this we'll jump back into index.css we'll take a new line we will say text area and we'll add a pseudo element of focus and open our braces the styles in here will apply to any text area which is on focus we just need to add outline none and save now if we click this you can see the blue border has disappeared and it looks much nicer okay so now we just need to style this button we'll create a new class called save remember we added this to our button in the component and add node.js down in here so for this we will say background color we will give it a gray color we will remove the border we'll add a border radius of 15 pixels and we'll add some padding so we'll say the top will be 5 pixels the right will be 10 pixels the bottom will be 5 pixels and the left will be 10 pixels now if we see this you can see it's updated in the browser and we've made this nice grey button with some rounded corners which matches the theme of our app but you'll notice that if we hover over it or it doesn't indicate to the user that it's clickable so we just need to add some hover styles so say save and this time we use the hover sudo element open our braces and we will add some styles in here that will apply when the user hovers over the button so add a background color which goes slightly lighter so we'll say and we will add a cursor pointer to change the cursor now if we save this and if we hover over it you can see the color changes and the cursor changes okay so now we have our add new note component created however it doesn't do very much yet so we will add the javascript to make it all work okay so now we will look at how to save and note before we can save the note we need to know what the user has typed to do this we will use the use state took to save the value so if we jump into add note dot js we will take a new line just inside the function and we'll type const common braces note text comma set note text and this is going to use state and we're going to default it to an empty string since that's what we want our text box to start off with so it's telling us that you state is undefined which means we need to import it so we do import braces use it from react okay so now when the user types into our text area we want to update the state value we can do this by adding an unchanged property and passing in a function so in our text area we will say unchange is going to be equal to handle change so we have to create this function which we'll do up here so we'll take a new line just beneath our state objects and we will type const handle change is going to be equal to an arrow function and react will automatically pass in the event as an argument which we can get up here so we'll call this event and this gets passed to your function the event has a bunch of stuff in it but the main thing we care about is the target.value this is the value of the text area in other words what the user has typed so if we log this out for now we'll say console.log event dot target dot value and we'll save this if we check this in chrome so we'll open up our dev tools every time we type you can see it gets logged out so this happens on every keystroke which means the value will get updated on every keystroke instead so we'll close this down and jump back into your code now we have the value we can set this to state by calling the set note text function given to us by the use data so in our handle change function we can delete the console log and we'll say set note text and we want this to be the event target dot value which we just seen so now our state will get updated every time the value of the text area changes when the user types we also want to add value equals note text in our text area as this gives us a bit more control over what the text area value is so for example if we wanted to set a default value in here or reset the text value we can simply update state and the changes will take effect next we want to save the note when the user clicks the save button we will add a property to the button called on click this will be a function call which we will name handle to click and up here we will create the function const handle see if click is going to equal to an arrow function okay so what we want to happen in this function is that the state gets updated with the new note since the state lives in the top level component in app.js here the child component add note.js does not know how to update the state to solve this we need to pass a function from the parent in this case app.js which allows the child to update the step so in other words we create a function in app.js and then we pass it down through the components and add nodejs can use this function to update the state in app.js so in app.js just below our state values we will create a new function called add note and this will be an arrow function and it's going to accept the text that the user has added for now we will blog a message to the console saying what the text will be so say console.log text and we will come back to actually updating the state in a minute first we just want to make sure our add note component can call this function by passing it down through the components so we will pass this to the notes list as a prop which we'll call handle add note and we'll make this equal to our function in the notes list component we can destructure this to get access to that function via the props and we will pass it to the uh note component so we'll say handle add note it's going to go to handle add note now in the add node component we will structure the props again to get this function so now our add node function has access to the handle add node function we can simply call it from our event handler function in here so we'll say whenever handle c of click gets called by the button we want to call handle a note and we'll pass in the text of the note which in this case is our note text variable that we stored in state so if we paste this in so if we save this and we will open our dev tools again if we type a note so say hello this is a new note and if i click save you can see it appears here this means our component is able to call the add node function correctly so close this and and jump back into our ide so the last piece of this puzzle is to actually update the state with the new note so in app.js the add note function which currently takes the text we will build up a new note object which has the same properties as our other notes and see if this just did so in add note function we will delete the console log because we don't need that anymore and the first thing we'll do is create a date object so constitute equals new dit remember each note has a date assigned to it next we'll create a new note object so say const note would be go to an object and then we'll say the text it's going to be whatever text gets passed to us from the add note component which captures the user input so we'll say text and take a comma next we'll say dit and we're going to take the date from the date object we created up here so we'll say date dot to local date string so this will convert the date to whatever format that your local country understands and the last thing we want to do in here is add an id so we'll say id and then we will just call the nano id function to generate a new id like so now we need to create a new array that has the existing notes and we'll add the new note on to the end of this array so we'll say const new notes it's going to be equal to open some square brackets and then we're going to use the spread operator to copy the current array of notes we'll take a comma and then we will add our new note on to the end by typing u note so we need to do this as it's bad to mutate state in react this line creates a new array instead of updating the old array finally we just have to call the set notes function and pass in our new notes which will update the state so at this point when we call set notes this causes the components to re-render and the list updates with the new data so let's bring our browser back into view if we add a new note in here by typing something in the box and if we click save you can see that it works and the note gets added and it adds the current date and it has the bin icon as well okay so i want to spend a second to talk about how we pass the add note function down the tree of components so we define it in app.js and we pass it through the notes list component and into the add note component this is what we call prop drilling the process of passing things through components until it gets where it's needed so this is a perfectly fine approach however if you find yourself doing this for many components this is when you might consider using something like the context api which prevents having to pass your data through a bunch of different components okay so before we move on to displaying the character count there are a few tidy up things to do firstly the user can add a blank note or a note with empty blinds which is no good so if i take some spaces in here and click save you can see it gets added or if i hit enter a few times i i have all this extra space but it still gets added as well and secondly the text on the add note component stays even after we have added our note which is no good leader so if i add a note in here and click save the note gets added okay but we still have the text here which means the user has to go in and delete the text if they want to add a new note so if we jump back into our ide we will have a look at the first issue so we'll jump into add note js we basically want to check if the value the user has typed is valid before we save it to state so the best place to do this is in the handle save click function so in here we want to take a new line and then we'll say if note text dot trim the trim function removes white space from the start and the end of a string and then we want to check the length so do that length if the length is greater than zero that means after the white space has been removed we still have some text which means we can go ahead and call our handle add note function which will update the state so we'll just paste that inside the if block if the note text is empty then it's just going to skip over this block and not do anything so now if we bring up our chrome again and we'll try this so if we add some space and click save you can see it doesn't do anything and it doesn't save to our app and if we add a bunch of spaces and click save you can see it still doesn't do anything so that's the first issue fixed so the next issue is that we want to reset the form after a note gets added so we'll go into our id and after we've added a note we just want to set the note text back to an empty string like so so if there is a value it's going to get added to state and our input should hopefully reset so let's try this one in the browser here we will add a bunch of text like so and if we save you can see the note gets added and our note resets again okay so the next feature we will add is the character count so if we look at our finished example when the user types into the input you can see the character count goes down on each keystroke if we paste some stuff in here once we get to the character count limit we cannot type anymore if we press backspace or delete the text the character count updates to reflect so if we jump into our code and going to add note.js you can see in our jsx that we have a hard-coded value of 200 for the characters remaining what we want to do is update this to reflect the amount left okay so to do this we'll create a plane variable at the top of the function called character limit and this is going to be equal to 200. we don't need to put this in state since the user cannot change it if we added a feature where the user could change this number it would be a good idea to hold it in state but for now this is fine remember we store the note text in state which means that we have easy access to whatever the user has typed in the form of the note text variable so now in our jsx we can delete the hard coded 200 and open some braces in here we can say character limit minus note text dot length this subtraction will give us the characters remaining if the limit is 200 and the note text that length is 100 that means there are 100 characters left so now if we save this and open it in our browser if we type some stuff in here you can see the character count has started to go down and if we backspace the character account has started to go up so this works because every time the user types remember we are updating the state with the new value they have typed the handle change function gets called and in here we set the state to the new value when we set the state react re-renders the component meaning this little subtraction gets run again displaying the new value okay so this almost works but if we type over the limit the count goes into the minus so if we piss some stuff here it says we have two characters left but if we keep typing it goes into the minus and it just doesn't work very well what we want to do here is prevent the text from updating if the new value the user has typed goes over our limit of 200. so we'll jump back into our code it makes sense to do this in our handle change function as this is where the value gets saved to state if we add an if statement in here and open our braces and here we're going to type character limit minus event dot target dot value dot length and then we're going to say greater than equal to zero so what this does is subtracts the length of the value that the user has typed from the character limit if this value is greater or equal to zero that means the limit has not been reached or is at its maximum so we can go ahead and update state with the new value that the user typed so we get the value from the event which is what event.target.value is if the subtraction is a minus that means the user has entered more characters than is allowed or has reached the limit so it's just going to skip over this condition block so add some braces here to make sure our code is consistent and we'll paste the set node text function just inside so we'll save this and we'll jump back into chrome and we'll try this out so now if i paste something in here with two characters remaining and i try to type it's going to let me type two more characters but after that it sees that the limit's been reached and i can't type anymore this is a good example of how to do checks and validations in your app if you need to do a check before saving something to state doing it in the event handler function is a good idea okay so far so good however there is one issue that we need to look at if we add a new note and take a few lines so we'll add some more stuff with some line breaks in between if we save you will notice that the line breaks are lost to fix this we will jump into index.css and add a new property to our note class since we rendered a note text within the note div we just need to tell this div to keep the line spacing when it renders the text to do this we will say white space pre wrap this just means keep any spacing that was added to the text now if we save this you can see the spacing has now appeared on the note and we'll add a new one just to make sure if we save the spacing has persisted here as well now we can add notes the next thing that makes sense to do is delete a note so in our app each note has a little delete icon when this is clicked we want to call a function that removes the note from state so in app.js we will create a function called delete note so just below the add note function we'll do const delete note and this will be equal to an arrow function this will accept the id of the note to be deleted remember each note in our array has an id and when we add a new note it generates an id as well so in our delete note function we will use the filter function on the notes array so we'll say root stop filter to remove the note that has the same id as the id that was passed in so we'll pass in our function to the filter function this gives us the current note for the condition we will say note dot id is not equal to the id that was passed in so the filter function returns a new a so we don't have to worry about creating a new array like we did before when we added a note instead we can just assign this to a variable so we'll say const notes is equal to our statement here lastly we will set this instead by saying set notes and passing in our new notes array similar to the add note function we will pass the delete note function down to component tree so it can be called from the note component we will create a new prop in the notes list called handle delete note and this will be equal to our delete note function that we just created so in notes list dot js we can destructure this from the props so we'll say handle delete note and we'll pass it to the note component as a prop so we'll say handle delete note it's going to equal to handle delete note so we're just passing this down the tree now and note dot js we can do the same thing so we'll just destructure the handle delete note function from the props so when our delete icon or the bin icon is clicked we want to delete the note so we'll add our on click properly to the icon so say on click is equal to and this time we're going to pass in a function and then we'll say handle delete note and we'll pass in the id of this note which we get from the props up here so sometimes it makes sense to just call the function directly from the on click property like so this is because we do not have any other logic here okay so if we save this and we will try it in the browser so if we delete this note it gets removed from state and get removed from our app so it looks like our so it looks like our little delete icon doesn't have any hover effects which is bad as it doesn't indicate that this element is clickable we can fix this by adding some css so we'll jump back into our code and we will have a look at the delete icon and see if we've added a css class and we have we can add our styles to the delete icon class if we jump into index.css and we will scroll to the bottom and then we'll say dot delete icon to create the css class next we'll just say cursor pointer and save and now anytime we hover over the delete icon you can see the cursor changes to a pointer this is just a better experience for the user as it indicates that the icon is clickable okay so our little app is starting to shape up we can view add and delete notes next we will look at searching for a note by typing in a search bar so as you can see in the finished example in the top section here we have the search bar if we type into the search bar you can see that the notes start to filter based on the note text so if the user has typed new note then it's going to look for all the notes that has the text of new note and as we backspace you can see that the notes start to appear again if we delete the search value completely all the notes appear again so we'll start this one by styling the search bar so in our code the project tree we're going to go to source components folder and add a new file called search.js and save and here we will create our component so the import react from react and we will create our arrow function so say const search is equal to an arrow function and then we'll return some jsx so we'll put some divs in for now and we'll export our component export default search so to achieve the style of the search bar we are going to have a container div which is this grey bit here that holds an icon and an input field we will then style these things so that it looks like a fancy search bar in our code in search.js we're going to add a class name to this div called search this will be the container and we'll hold the icon and the text input so we'll add those next inside the dev we'll take a new line and we're going to type md search and give this a class name of search icons and we'll say size it's going to be equal to 1.3 em we'll close this now it's in md search is not defined that's okay because we haven't imported it yet so md search comes from the react icons pack similar to the delete icon that we used previously so the import open braces md search from react icons slash md and now we will add the input this is what the user will type into to search for the notes so the input just below md search and then we'll say the type of this is going to be text and the placeholder we will say type to search okay so this is the basic parts of our component so now we will render this in our app just to see how things are spaced out so in app.js in our return statement of the app function we want to take a new line just inside the container before the notes list component and this is where we will call our search component and it's saying search is not defined so if we scroll to the top of the import search from components slash search and if we save this let's have a look and see how things look so far so i'll space these windows out a bit and if we go to our app you can see we have our icon and our search bars appeared okay so now we will style these things so that they look like our finished example so the first thing we'll do is style the container which is the gray bit and we will we will position the icon and the search input so that they're nice and neat we'll keep this open so that we can see how things look as we're coding back in pager studio code i'll go to index.css and we'll scroll to the bottom i'll take a new line and then we'll type search remember this is the class that we added to our components container div in and the first thing we'll do is we will say display flex so this means that the search div is going to be a flexbox container which will help us position the search icon and the text box a bit easier so if we save this you can see things have started to align already which is good then we'll say align items center just to make sure things have aligned vertically let's say background color is going to be equal to this gray color we'll just add an rgb value for this now if we save you can see it's appeared next we'll add a border radius of 10 pixels this will give us the rounded edges here so i'll just zoom out a bit so we can see better and we will add some padding so that the icon and the input aren't pushed too close to the end so it's a padding of 5 pixels and then we'll say margin bottom of 1.5 em to give us some spacing okay so if we expand this out you can see we have a search bar so it looks a bit funny on small screens because i am zoomed in on chrome here quite a bit so that you can see but if we expand it out you can see that everything is aligned we have some nice borders which match the style of our app and we have an icon and a text input which is nicely aligned as well so now we can style the text box so that we hide the border and change the color to match our gray search bar we will say search space input we want to apply these styles to any inputs that are within the search div only so say border none which removes the border and the background color is going to be the same as what we said for our search input so we'll just copy this and we'll paste it in like so and as you can see this has changed the color to match so it's looking good so far however if we click on it you can see we have this blue outline which is no good so in index.css we'll take a new line and this time we're going to say search input and we're going to use the pseudo element of focus which applies any styles to a focused element or in other words that the user has clicked on so in here we'll just say outline with a value of none and if we save this we will expand our window if we click on it we still have the cursor that's blinking which tells us that we can type in here along with the placeholder and you can type stuff in okay so now we have our search thing in place we can start adding the logic to make it work if we think about what we need to do here first we need to capture the value that the user has typed next we need to filter the notes to only show the notes that contain that text we will focus on capturing the input value first similar to before we want to use a state hook to store the value that the user types we could put this hook in search.js as that's where the input is but since our notes list will rely on the state it makes sense to keep these pieces of state close together so instead we will put this hook in app.js jump into app.js and just below the new took we will create our own hook to store the search text so down here we'll type const open our square braces and we'll say search text comma set search text and this is going to equal to usd and set the initial state to be an empty string now we can pass the set search text function to the search component as a prop so down in here we will say handle search note and this will be go to set search text this is our hook function that lets us update the state of our search text in search.js we can destructure this prop to get the function so we'll add our curly braces in here and then we'll say handle search note in our input we can do an onchange property and pass in our function [Applause] so this function will be an arrow function which takes the event that gets passed in by react and then we can say handle search note and we will get the value that the user has typed from event dot target dot value so anytime the user types into the input it's going to call the handle search note prop which has a function to update the state tonight we'll jump into chrome and we will open the dev tools just to check that our state is getting updated so in the components tab here you can see our app tree so our status in app.js and over here you can see our two state hooks so the first one has the array of notes and the second one has our input value so if we type into our search bar you can see the state updates to reflect whatever it is our state hook is working and we now have easy access to what the user types in the form of the search text value which we stored in a hook in app.js now what we can do is filter the notes based on the search term before it gets passed to the notes list so in app.js we have the nukeslist component and we pass some notes to it so in here what we can do is say notes dot filter and we'll pass on our function which gives us the current note that the filter function is currently on and then we will say note.text which is the text of the note to lowercase dot includes open braces and then save search text so what this will do is take the current list of notes filter those notes to return only the ones that include the search text and the search text is what the user has typed into the search bar it will then pass the result of this to the newsless component as a notes prop now if we try this we will try searching for second and as you can see it's only returned this note that has the text second in it as we backspace you can see it resets to return all the notes since we don't have a search term so why do we filter the notes here at the point when we pass the notes to the notes list component well by doing this it means the original list and state up here is unchanged so if you have another component that also relies on the full notes list it won't be affected by any filtering this is also a good example of why we keep related pieces of state as close together as possible by doing this we have made it easy to filter the list based on the search text and the next developer coming along can easily see what's happening and find the different state things that they need next we will look at adding the header that you can see just above the search bar so we have a title and we have a button that toggles dark mode on and off so in our code we will jump into the components folder and add a new file called header.js in here we will create our component so import react from react and create our function so we say const header is equal to an arrow function and then we will return some jsx and we'll put an empty div in here for now and we'll export our component so export default header so in the return statement similar to what we did before we will create a div that will act as the container to help us position and style the title and the button we will give this div a class name of header inside this div we'll add a h1 tag and we'll add our title which is notes and finally we'll add our button and we will add some text which says toggle mode we will just reuse the styles from the save button which we can see and our note is a gray grounded shape so we'll just use these styles to prevent us creating any more styles so in button we'll say class name is equal to save you can change this name if you want and index.css to be more descriptive but for now i'm just going to leave this like this so now we have our header component which has the different elements in it we will render the header in app.js in app.js just above the search bar we will open our angle brackets and type header and check that it's been imported and it has if not you can get it from the components folder so we'll bring this in a bit so we can see what things look like currently so we have our title and our button has appeared now we need to style the header container div to space these things out evenly so if we jump into index.css and just above the container class we will add a css class for the header so this is what we added to your component and in here we will say display flex to say that the div that has the header class as a flexbox container and then we'll say align items center so as you can see this has dropped the notes title and the toggle mode button onto the same line and align them vertically now we just need to say justify content space between so this will push each of our elements to either end of the container in this case to the left and to the right so if we expand the site you can see the toggle button always stays to the left-hand side okay so while we are in index.css adding some styles we will go ahead and add our dark mode styles so in our finished app you will notice some things happen when we toggle dark mode the background turns to black and the title changes from black to white so in our index.css we will take a new line just under the header and we'll add a css class for dark mode and what we want to do in here is say background color black so anytime we apply the dark mode class to any of our elements in this case we'll be applying it to the top level div it will turn the background color to be black we also want to change the heading to be white so we'll say dark mode h1 to select any headings within the dark mode divs and we'll say color white so this will just turn any text to be white now that our header and our styles are in place here we can we can go ahead and add the logic to set dark mode remember it's a good idea to put things that change in state so we will use a state hook and store a boolean value to indicate whether the app is currently in dark mode or not we will do this in app.js as that is where the majority of our state lives so just below the search text state hook we will create a new one so we'll say const open braces dark mode comma set dark mode and this is going to be equal to u-state and we will set an initial property of false since there are are only two possible options either the app is in dark mode or it isn't in dark mode a bullion makes sense here now we will pass the set dark mode function to your header component so we'll call this prop handle toggle dark mode and this will be equal to our setter function for the dark mode static so set dark mode now in the header component we can destructure this prop to get the function so we'll add our curly braces and say handle toggle dark mode and we can call this in the on click property of our toggle mode button so let's say on click it's going to be equal to an arrow function and we'll call handle toggle dark mode like so when we call a setter function provided to us by a hook we can get access to the current state value by passing in an arrow function with an argument so in here we can add an arrow function and then we'll call this previous dark mode now in the body of our arrow function we want to set the new state to be the opposite of what the current dark mode value is so to do this we can use exclamation mark and then we'll say previous dark mode if dark mode is currently true we're going to set it to be false and vice versa and we will fix our mistake here just to make things into camelcase so we'll change the lowercase m2 and uppercase m so if we open up our browser if we click this nothing happens but if we look at our dev tools you can see we have a new state hook that has appeared this one is our dark mode value so it is initially set to false if we click toggle mode it gets set to true if we click toggle mode again it gets set back to false even though our ui hasn't changed yet our state is updating which is what we want now what we want to do is dynamically apply the dark mode class to your app depending on if the dark mode state value is set to true remember we added classes for dark mode in index.css already so in app.js we will create a new div and put our existing jsx within it so we'll create a div and we will just paste everything that we previously had inside this new div so this allows us to add styles to the overall app without affecting the styles and the layout we have created so far in the existing container div so now we'll add a class name and this is going to be equal to a template string open some braces and add some back text within it so in here we are going to use a template string to determine if we should dynamically add the dark mode class to the step based on our dark mode state variable so in our div just inside the back text we will type a dollar sign open our braces and then we'll say dark mode double ampersand open quotes and then we'll type dark mode so what this is going to say is if dark mode is equal to tree then add dark mode to this string this line of code will create a string and depending on the status of dark mode it's going to append dark mode or not and then it's going to apply the result of this to the class name of this div so now if we try this you can see if we toggle it changes the background to black and it changes the text depending on if the background is black or not so it looks like we have one bug to fix and that is that the background isn't taking up the whole screen so it looks like we need to tell the inner container to take up 100 of the view and that will push everything down so if we jump into index.css and if we go down to the container css class we will say min height it's going to be 100 v h so this just means 100 percent of the view height and hopefully if we try this again it should work and it does so the gap has disappeared okay so the last feature that we want to add to our app is that we want to save and retrieve from local storage so that the data persists even after we close the tab so the best place to do this is an app.js since this is where our state lives so what we're going to do is just below our state objects we're going to add the use effect hook so the use effect hook lets us run code at certain points of the component's life cycle or if certain variables change so it's telling us it's not defined so we just need to import it so at the top just beside the used import we will say comma use effect so this effect is going to save our notes to local storage anytime the notes change so in here in the function we will say local storage dot set item and open braces so we have to give it a key the key is a string which we will use to retrieve the notes later so we'll say react dash notes dash app dash data just to make sure that it doesn't conflict with anything else and outside the string we will say a comma and this second parameter that we pass into the set item function is the data that we want to see it it's a good practice to stringify any data before we save it to local storage so we'll say json dot stringify and open our braces and now we'll pass in our notes and if we see it what this will do is save the notes to local storage with a key of react notes app data now you can see that the use effect is saying missing dependency notes so you just need to type notes in here so what this little array is is this is the dependency array so anytime something in here changes so in this case if the notes variable gets updated with new notes then it's going to trigger this use effect function which means the new nodes will get saved to local storage so if we try this we can go to our app and if we go to the application tab you can see down the left here we have the local storage drop down if we click on google host you can see our data has appeared so the key is react notes app data and the value is a string if we look at this preview thing down here you can see it has our node data so it has the array and it has the four notes each one with um a date an id and a text so that means we have persisted our data in local storage so if we jump back into our code this is a good approach as any time the notes array changes this will trigger automatically this means that if you were to add new functionality say edit a note you wouldn't have to manually do this because as long as you were saving the new notes to state in the state array this use effect will get triggered okay so now that we have saved the notes what we want to do is retrieve any notes that are saved into local storage when the app loads so again we can use the use effect function for this so we'll create another one [Applause] we'll type this effect open our braces and add our arrow function just to say the arrow function we're going to do a comma and add an empty array when the dependency array is empty and i use a factoric that means it's only going to run on the first load and it won't run after that which is what we want because we only want to try and retrieve any notes on first load so in here we can say const saved notes and it's going to be equal to json.parse because remember we save the data as a string when we set it so whenever we would retrieve it we have to parse the data into a javascript object which is what this does and in here we will save localstorage.get item and then we simply pass in our key so our key is react notes app data so we can copy that paste it in like so and that's it so the app is going to load it's going to go to local storage and it's going to retrieve the data that has this key that's going to parse it into a javascript object and then it's going to save it to this saved notes variable now the last thing we need to do is check if we received any notes from local storage and if we did we want to set it into it so say if saved notes this means if any of the notes were retrieved successfully from google storage there will be a value if there is a value we will do set notes and pass in our notes like so if saved notes is empty or doesn't have a value it won't set it to state and it will skip over this conditional so now if we try this if we jump into chrome and we will close this for a minute and we have some notes that are in our app already so if we add a new one this is as received note and click save it appears here now if we refresh the page hopefully it works and it does and if we try going to a different tab you can see that it has saved here as well the beauty of using a use effect hook to handle the saving and retrieving of the notes is that we can delete the note or two or three and if we refresh it's going to automatically save and retrieve those notes from local storage and that's because we have told these use effect hooks here to automatically see if whenever the notes array has changed so whenever we delete a note it changes the notes array and this triggers us to run that about wraps it up for this video if you enjoyed watching it would be great if you could like and share this video as it really helps the channel if you added your own features don't forget to tweet me it's always good to see what you come up with you can find different ways to contact me in the description thanks again for watching and i'll see you in the next one you
Info
Channel: Chris Blakely
Views: 8,333
Rating: 5 out of 5
Keywords: react notes app tutorial, react notes tutorial, react notes list tutorial, react notes list, react beginner projects, react and css tutorial, react project for beginners, react project ideas, notes list react, react notes app, react for beginners, react and css, css react, chris blakely
Id: 8KB3DHI-QbM
Channel Id: undefined
Length: 85min 0sec (5100 seconds)
Published: Thu May 06 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.