Here, I have a simple React app that receives a hard coded array of to-do objects as a prop, a mapping over the to-dos, and rendering a list item for each object in the array.
Everything appears to be working fine, but if I open up DevTools, and I reload this, we're going to see that I get a warming in the console that each child in the array or the iterator should have a unique key prop.
It's happening here as we're rendering this LI for each item in the list, React is looking for a key on this parent level tech. I can fix that by just adding key, and I can use the to-do items' ID property as the key. I'll clear the console, and reload, and we'll see that this time, there's no warning in the console.
Now, that I've fixed that error, I want to continue refactoring this component. We're going to take this function that I'm mapping over, and I'm going to extract this out into its own stateless functional component.
I'm just going to cut this here, and write in the same file. I'm going to declare a constant, and I'm going to call it a to-do item. I'm just going to paste that right back in. Now, I have this to-do item component that I can call "write in my map."
Now, I'm going to clear out the console, and I'm going to reload my view, and everything looks just fine. If we jump over to the React documentation, we'll see that this is actually the wrong way to put a key on an item. The right way is listed below, and the way we should do that is inside the map, we should specify our key directly on our new component.
If we jump back over to the code, we can fix this by wrapping this in a function that takes in a to-do, and then close our to-do item component with key, and we'll make that key property our todo.id. Then we can spread the rest of our properties out for our to-do.
Then I can come up here, and I can get rid of this key because it's actually not doing us any good. I'll clear the console, and reload, and everything is working as expected. Now, the problem here is that I've basically created an unnecessary function just to get this one property on my item.
There's a better way to do this. The solution here is to create a higher order component that will essentially do exactly what we just did with our function, or wrap it up in a nice reusable way, and get it out of our application code.
I'm going to cut this, and I'm going to come up here, and I'm going to declare a new constant, and I'm going to call this with key. A higher order component is just a function that takes in a component, and returns a new component.
In our case, it's going to be a stateless functional component. I'm going to paste in what I have here, and I'm just going to make this a little more generic. To-do here is going to become props, and then I'm going to update this to be props.id, and then spread the rest of the props.
Because I'm taking in my component with the name comp, I'm just going to use the same thing here. Now, I have this with key function that I can pass in any component, and that will return a function that's waiting for props which will return a rendered component with a key using the ID value from the props.
Now that that's defined, let's use it on our to-do item component. I'm going to drop down here, and I'm going to declare a new constant, and I'm going to call it to-do display, and I'm going to set that to equal, or call to with key.
Then I'm going to pass that in to-do item, and then I'm going to use this to-do to display component in my map. Now, everything works as expected, but we don't have to actually put the key property in the JSX. We can just map over a function.
This works just fine, and it solves our problem, but it assumes that we have an ID property on our props, and that might not always be the case. I'm going to come here, and I'm going to just edit this a little bit, and I'm going to give this a second argument which I'll call prop name.
Prop name is going to be the name of the property, or the key that we want to get off of props to define the key attribute on our component. Now, I can just change this, and I can use bracket notation to get at my property by name.
I'll pass in prop name, and I can update this with a more appropriate name, so we'll call it with key from props. Then I'm going to come down here, and I'm going to update my call to with key to use the new name, and pass in ID as a string. When I reload, everything will be working as expected.