Alright guys, it's time for yet another challenge.
And in this challenge you're going to be applying a lot of things, not just the things that you learnt
in the last lesson, but everything up until now. And the end result is going to be this beautiful to-do
list, where you're going to be able to add some new items, say, "Buy milk", click Add, and it gets added to
a list of bullet points. And effectively this is what you're aiming for.
But in order to achieve this, then you might want to refer to the index.js and look at the challenge
text.
So the first step is when text is written into this input,
the state should somehow be changed so that we can track the state of what's in the input.
Next when the Add button gets pressed, whatever is currently inside the input should be saved and added
to an array.
Now finally, the ul or the unordered list here, should display all of the array items as separate
list items. And all of these combined together
you should end up with this functionality.
Go ahead and fork
the starting project for this lesson.
Look around the App.js and the index.js to see what you've got to work with, and then pause
the video and try to complete the challenge. Once you're done, head back over here and I'll walk through
the solution with you. Have a go now.
All right.
So the first thing we're going to tackle is when new text is written into the input, it's state should
be saved.
So how can we do this?
Well we've done this quite a few times.
We are going to create a new const in our App function and we'll give it a name that makes sense,
let's call it the inputText, and then maybe we'll have a function to set it called,
setInputText.
And it doesn't matter what you've called yours as long as your code works it's fine.
And then we're going to of course invoke useState to be able to change and save the state of the input
text when it gets changed.
useState of course requires the import up here and then we get to define an initial value which I'll
set to just an empty string because the input is blank
to begin with. Now let's think about where we would want to use this inputText.
Well in this case, we actually don't have an element onscreen where we're going to be able to see the
state changes for inputText,
but what we should do though, is to add a value property to our input and to tie it to this inputText
state.
So this way we are controlling the inputs and keeping the input value inline with the inputText.
The next thing to do is, when do we actually want to change the inputText, when do we want to call this
function?
Well it's probably going to be when this input gets changed.
So let's add an onChange() to this input and get it to call a function which will call handleChange(),
and then let's add this function right here, handleChange(), and then we get the event that's going to
be passed to it.
Now the next thing to do is to create a new const which is going to keep hold of the new value of
the input.
So we'll just call it newValue
and I'm going to set it to event.target.value.
Now notice how in this case we've only got one input.
So we don't actually care about the name of the input, so we don't actually need to use destructuring
or anything fancy.
We just want to get a hold of the value of the target that triggered the event.
And once we've gotten that, then we can call setInputText and inside this function, we're going to
pass it this new value. And because in this case I'm not actually changing the inputText,
I'm not mutating it using setInputText,
I don't actually need the previous value.
It doesn't really matter.
I've got this whole new value that I want to give it, which is going to correspond to whatever the user
is typing inside this input.
So now how can we confirm that this is actually working?
Well, we can go ahead and pop it out and then use our Chrome Developer Tools to check the state of our
App.
So we've currently got one state being tracked and it starts out being equal to an empty string.
That sounds about right.
And as soon as I start typing, you'll see that the state updates to reflect the latest state.
So now that's being kept track of inside this inputText.
So we've now achieved part 1. Part 2 involves tapping into the moment when the Add button is pressed
and then passing the current data in the input to an array.
So how can we do that?
Well, the goal is to keep track of all of the items that the user has added inside an array. So we can create
a new const which is going to hold an array of items, so we can call it items, to-do items, whatever it
is you want to name it,
and then we're going to provide a name for the function which is going to set these items,
and of course we're going to use state.
Now the initial value for items is just going to be an empty array.
It's not going to hold anything at all. And this means that when this to-do list starts out we won't
actually see any bullet points.
So if we want to use this array of items to render all of the li's to contain the text inside each
item, then we would probably have to use what we learned about the map function. So we can tap into our
items, call the map function and then inside the map function we can pass it an arrow function, and inside
this arrow function we'll get access to each item, or if you want to make sure that there's enough difference
between the array and the individual item, you call it todoItem if you want. But effectively just be
sure that you know that this is going to be the singular form that comes from mapping through each of
the items.
And what we're going to return is an li which has the todoItem as the text in the list item. Now
notice how this syntax is not highlighting at all. It's not giving the different colors,
and the reason, if I hover over it is it says, unexpected token. It doesn't recognize this as code.
So if we highlight all of this and try to add the left-hand curly brace, it'll add the curly braces around
this code and it'll get interpreted as JavaScript.
Now we can of course simplify this down even more, because we're only returning a single item so we can
get rid of the return keyword,
the opening curly brace, the closing semicolon, and the closing curly brace to end up with just this. So
map through all of the items, for each todoItem, create an li, and put the todoItem in that li,
is what we're telling our code to do.
Now all we have to do is call setItems when the Add button gets pressed.
Now the Add button is of course this one, and we can simply add an onClick to it and get it to trigger
a function.
You can either get it to trigger an inline function so you could add simply some sort of arrow function
here. Alternatively, and what I would prefer just in case we need to reuse this functionality, I'm going
to call a function code addItem. And then we get to create this addItem() function up here, and then inside
this function we're going to add whatever text that is inside our inputs into our items array. Now
to do that, we of course have to call setItems.
So let's call setItems,
and inside here we want to get hold of the previous items so that we can simply add the new input at
the end of the array as a new item.
So to do that, we're going to have to create a new arrow function. Inside the input of this arrow function
is going to be the prevItems or prevValues whatever it is you want to call it.
And then what we're going to return is going to be a new array using the spread operator, yeah
that's what you learned in the last lesson,
you can spread arrays, you can spread objects, you can spread butter,
it's really really handy.
So let's add our spread operator, add in all of the items from our prevItems, and then we're going
to add the new item which is going to be what's currently inside are inputText.
So I'm going to leave my code as it is now, and I'm not going to simplify it further by removing the
return because I think this reads much better, and it's already so short anyways.
So now let's go ahead and give our app a spin and see if it works.
Let's check our state.
We've got two.
One is holding a single value,
the other is holding an empty array.
Let's go ahead and start adding some text, "Buy milk". That is being held inside of value which is the input
text.
And then when I click Add, you can see that our array now has a new value. At index 0,
it's got by milk. And because the array now has a value
the state of it has updated which triggered the map function to create a new list item.
If we decide to add another item say, "Buy egg" then our array also gets updated and we now have this beautiful
functionality. So the last thing that you might want to do is just to clear the input when you click
the Add button ready for the next item.
So the place to do that is probably going to be inside addItem so that once you've sent the inputText
to the items array, then after that has completed, you probably want to set the inputText to an empty
string again. So we can call setInputText and pass in an empty string, just like that. Now whenever we
type in something, "Buy milk", hit Add, it gets added and then it gets cleared. Pretty neat right? Now
did you manage to complete this challenge?
If not, be sure to head back to review your code and once you're happy with how everything is done, then
head over to the next lesson and we're going to talk about how to manage a component tree and how to
pass state around in our component tree.
Notice how at the moment we've only got one App component, and that's to try and keep it simple so you
can see what's going on, because there's enough complex things going on. But in the next lesson, we're
going to level up even more and we're going to divide up our app into separate components so that we
can pass state from child components up to parent components. As you can imagine,
I'm going to need you to really understand everything that's already been covered so that you'll be
comfortable in the next lesson.
Take a breather, take a moment to pause, have a think, go for a walk, have some food and once you're ready
I'll see you on the next lesson.