Next lesson playing in 5 seconds

  • Overview
  • Transcript

3.6 Challenge: Todo List

Everybody builds a basic to-do list app, right? You’ll code one too in this challenge. Specifically, you’ll need to pass data to a child component where it will be updated and sent back to the parent component.

Related Links

3.6 Challenge: Todo List

Welcome to Challenge 5. In this challenge, we're going to do what is kind of the Hello World example of learning to program with a front end library, it is the Todo List. So we have two components already built here, we have a TodoList component and we have TodoItem component. And the to-do list component just has a list of to-dos in its state and each one has a text property and a done property, and then we can render our list of to-do items in the to-do item component. And then in here we just render a list item here with an input box which has the value. And if this to do item is done then the value is not editable, it's read only, and then we have a button here which we can use to Finish or Unfinish a task. Now right now, you can see its rendering just fine over here on the right side, but we can't actually change these things in the state of our ToDoList component. So if I try and, I don't know if you can hear me typing, but if I try and make changes in these input elements nothing happens. If I click finish, notice that the text on these button doesn't change as it should. And I can still select text in here and assuming that I could actually make changes it would be editable, so all of that needs to be wired up correctly. Let's go ahead and fork this code pen, and you can find a link to this code pen of course underneath this video. If you'd like to go ahead and try and solve this yourself, pause the video now because I'm gonna go ahead and show you What MY SOLUTION is. Okay, so we have a couple of different things we need to do here. First, inside of our TodoItem here, we need to be able to make changes to this data. Which means we're gonna need to have an onClick handler for the button and an onChange handler for the input element. But then we need a way to pass those changes back up to the parent TodoList component. So that means that the TodoList here needs to pass a function down, to TodoItem, so that it can call that function. So we may have a updateTodo function here and we'll have to eventually pass it a function here. I'll just use kind of a blank function here for now, just know and will come back the changing that function later on. But for right now we have an update to do function that we can actually call, and what I'm gonna do, break this on the two lines. So that this all looks easy on the eyes there we go. Okay, so now we can use update to do up here within TodoItem. So how might this work? Well, let's start with the button, we want to have onClick here, and we'll to save this.handleClick. Man, we're running into all these line breaks today. Okay, so we need to write our handleClick function. Let's do this up here, we'll say handleClick, handleClick is going to make a change to this todo property, that was passed down to our todo item. When this is clicked, we want to reverse whatever the value of done is. So, if it's false, switch it to true, if it's true, switch it to false. And then, of course, we wanna pass that newly updated todo object, backup through the updateTodo function. So, we can get our newTodo by doing Object.assign, so that we don't mutate the data. And we will copy all the properties in our existing Todo, which is actually this.props.todo. But then we want to make sure that we do overwrite the done property, which should be the reverse or the negative of this .props.todo.done, so that's our newTodo. [COUGH] and then we can do this .props.updateTodo, and we will pass it our newTodo, excellent. Okay, so that's handling the click. Let's actually go down and write our updateTodo now, so that we can actually see this in action. Because right now, if we just change null here to be console.log(e), and if I pop up in the console we've got a bunch of errors here, right, because we had a input with a value, but no on change and that's still the case but we'll get back to that. So now if I try and click finish, you can see that our new to do object is being printed out here where done is set to true, but we're not seeing that reflected in the UI yet. Because right now, we need to save this new todo back into our todos state here. So let's go ahead and write an updateTodo function, and it's gonna take that newTodo as its parameter. And in here, we're gonna go ahead and do this.setState. Now, I'm gonna set the state in a way that you may not have seen before. We're going to pass a function to setState instead of passing an object. This is actually considered a good practice in react and may possibly be the only way to setState in the future. The function that you pass to setState will receive the current state as a parameter. So we can recieve that state as a parameter to this function and then we return Our new state from this function. This is actually a more reliable way of creating a new state, that is based on the old state. You can almost think of it as a kind of reducer function, within our setState call here. So we're going to go ahead and return a new object here. And since that's all we're gonna do from this function, we could actually. Just wrap these curl braces and parenthesis so that it knows that this is an object and not the function block. And let's see, we only have these todo's item. So let's get our existing state.todos, and we will nap over each todo there, and if the is equal to the, so, then we know that this is the todo object that we have replace. And so we can replace it with the newTodo, otherwise we can just return the old todo, because it's not the one that we're looking to replace. Okay, so we need an extra parenthesis there, there we go. Okay, so now we can actually update this state, so let's go ahead and see if we can see this in action. If I go ahead and click finish, we're still console logging. All right, that's because down here we need to change this to actually refer to this .updateTodo. So let's clear our console, let's go ahead and click finish. And notice that our finish button has changed to unfinish, and that's because ToDo.done is now true instead of false. Also, I don't know if you can see this but wash floor is now a little bit grayed out, and it's not actually editable notice I can't get a caret to show up in that text box because it is disabled, it's not editable. If I click Unfinish it's switches back to finish, and now I can get a caret in the box because this will be editable. If our input here had an unchanged handler, so that is our next step. Let us add an unchanged handler here, and that would be this.handleChange. Let's write handleChange, so in here we're gonna do the same thing, we'll start by creating a new todo. So we'll say, Let newTodo equal Object.assign. Copy all the properties from this.props.todo, and then handleChange will pass our event object, and of course and so in here we're gonna set the text to be And then underneath that we'll do this.props.updateTodo, and we'll pass it the newTodo. That is our handleChange function, let's see if this will actually work. If we come over here and try and change cat to dog, notice that it works just fine. We can now say buy eggs instead of milk, and maybe we can say wash the car excellent. So now, we are successfully making changes to an object in a child component and passing those changes back up to the parent component, where they can be restored. There's one thing I wanna do here, and that is you'll notice that the handleChange function and the handleClick function have law of similar code. And so, I've often had child components like this where we want to update an object in some way and then pass it back to a parent, and you'll find that a common pattern for doing that is using object assign in this way. And so what we're gonna do here is create a new function, and we're going to say send delta. Now, delta just being the term for whatever needs to change between this to do and the new to do that we need. So what we can do here is pass our delta, or our object for just the properties that need to change just send delta, and then let me go ahead and copy what we do to handle click here and paste this down below. The delta is basically the last argument that we've passed to object assign, right? So we can go ahead and replace that with our delta, and then we just send that. And we could actually make this add one layer if we want, but I think this looks a little bit cleaner this way. So now what does this mean for handle click and handle change? Well in handle click all we need to do is change this to the this.sendDelta, there we go. And since this only is one property, I think this looks much better if we put it all on one line, there we go. And we can do the exact same thing here this.sendDelta and now handle click and handle change are much smaller. We got to get rid of this extra line as well. And if you wanted you can even wrap this functions onto a single line which may look a little weird perhaps, but it could have kind of a nice symmetry throughout your application. So, you can just see what fields are changing in each place for each of your handle functions. You can see those properties just like that. Of course you may have come up with a different solution. So, that's great but either way we now have successfully create a child component that change it's data, and then send those changes back up for storing to it's parent component.

Back to the top