Right now, all of the markup for this application lives in the Render method of the app component. Let's refactor some of this markup out into individual components. I'll start by adding a directory under source, and I'm going to call that components. Inside components, I'm going to add a new file, which I'll call todoform.js.
In todoForm, I want to import React from react. This is going to give us our jsx support. Then I want to export default, and I'm just going to use an error function that accepts props. I'll use parentheses so that I can have an implicit return on multiple lines. In app.js, I'm just going grab this form markup, cut it, and paste it right into that return.
Now, I can just go back to app.js and I can import that component as todoForm from -- and I'll give it a relative path -- to components/todoForm. Then I can update my markup to just use todoForm. When I save that, the browser will reload, and everything still works as expected.
I want to continue this refactoring. In components, I'm going to add a second file, and this is going to be todolist.js. In todoList, I'm going to also import React from react. Then in app.js, I'm just going to grab this entire todoList div, and I'll cut it.
Then, again, I'm going to export default, and I'm going to use an error function that accepts props. Then I'm going to paste that markup in between my parens. I can save this, and then, back in app.js, I'm going to want to use todoList, just like I used todoForm. For that to work, I need to import it. Import todoList from component/todoList.
When the browser reloads, we'll see that we have an error. The problem is I copied this code directly out of app.js, which was referencing props from this, because it was a class component. What I'm going to do is, right in the browser, I'm just going to click on this error. That's going to open the offending file in my editor.
Now I can come in here, then I can reference todos off of the props, save that. We'll see that we have a new error. This time props.todos.map is not a function. The problem here is that todos doesn't exist, because we need to pass it in.
Back in app.js, we have access to todos off of the props of app. What I'm going to do is I'm going to say this.props.todos, and I'll save that. Now, the browser reloads and we're rendering our todos. I'm going to go back into todolist.js and do a little bit more refactoring.
I want to take this list item and I want to move that into its own component. Since I'm not going to use it outside of this component, I'm just going to define it right here in the same file. I'll do that by defining a constant, which I'm going to call todoItem. I'm going to set that to equal an error function that accepts props.
Then it'll return some jsx, and that jsx is just going to be this list item here. Then I'm going to use this todoItem component right down here in my map. For each todo, I'll render out a todo item. That todo item is going to receive the todo. I'm just going to do that by spreading the props of that object.
Then, up here, I'm going to reference these items off of props, but rather than replacing todo with props.ID, and then props.isComplete, I'm going to use destructuring. I'm just going to destructure that props value that's being passed in into ID name, and isComplete. Then I can just get rid of the qualifier on each one of these and I can save that.
I'm getting an error because I didn't close this tag down here, so I need to update my todo item here so that the tag's closed. Now, everything renders as expected. If I open the dev tools though, we're going to see that there is a console warning that each child of an array or iterator should have a unique key prop.
The problem is that this worked when I had the list item directly in my map, because I had this key property. Now that it's been moved into a component, I actually need to put it on the component instance that's being iterated over.
I'm going to cut that and I'm going to bring it down here into todoItem. I'll give that a key with a todo.ID as its property. Now when the browser reloads, our error's gone, and everything's working as expected.