Mapping Components in React

Mapping Components in React

We've already covered the hardcoding of components in React, when you already know exactly what you want to show up on the screen.

However, React isn't intended to be used simply for hardcoded data. Its whole purpose is to react to new information and user input and pass data to the components that need to know about the data change so they can update the UI accordingly.

React makes taking regular JavaScript data and turning it into an HTML element simple. One thing you'll find yourself needing to do is take an array of data and turning it into a series of HTML elements.

Imagine you're making a website to display a list of all your favorite songs. You already have an array in your JavaScript filled with strings of song titles, but you need to turn that array of strings into <li> elements.

This is where JavaScript's .map() method comes in! The goal is to take your array of strings and turn it into an array of JSX <li> elements that contain the text of the string.

When React sees an array of JSX elements, it knows to loop through that array and create new HTML elements for each JSX element in the array. This allows us to write a component like this:

function Items() {
    return (
        <ul>
            {[<li>Hello</li>,<li>World</li>,<li>How are you?</li>]}
        </ul>
    )
}

Which outputs this:

It's not very common to type out the array manually though. Instead, you'll usually have an array containing just your data and you'll use .map() to create the new array with JSX in it:

function Items()
    words = ["Hello", "World", "How are you?"]
    return (
        <ul>
            {words.map(item => <li>{item}</li>)}
        </ul>
    )
}

It may not be immediately apparent why this is helpful. In the future, however, you'll learn about how a user can programmatically add items to arrays, and we'll want React to handle updating the UI to show the newly added items. With the first, hardcoded array we had above, this wouldn't be possible.

Also, what if your data is more detailed than just simple strings. For example:

const turtles = [
    {
        name: "Leonardo",
        nickName: "Leo",
        weapon: "Katana",
        imgUrl: "https://upload.wikimedia.org/wikipedia/en/e/ed/Leonardo_%28Teenage_Mutant_Ninja_Turtles%29.jpg"
    },
    {
        name: "Donatello",
        nickName: "Don",
        weapon: "Bo staff",
        imgUrl: "https://upload.wikimedia.org/wikipedia/en/5/5a/Donatello_%28Teenage_Mutant_Ninja_Turtles%29.jpg"
    },
    {
        name: "Michelangelo",
        nickName: "Mikey",
        weapon: "Nunchucks",
        imgUrl: "https://upload.wikimedia.org/wikipedia/en/f/f3/Michelangelo_%28Teenage_Mutant_Ninja_Turtles%29.jpg"
    },
    {
        name: "Raphael",
        nickName: "Raph",
        weapon: "Sai",
        imgUrl: "https://upload.wikimedia.org/wikipedia/en/7/72/Raphael_%28Teenage_Mutant_Ninja_Tutles%29.jpg"
    }
]

Displaying all this data on a site is very easy in React using .map():

...


return (
    <div>
        {turtles.map(turtle =>
        <div>
            <h1>{turtle.name} ({turtle.nickName})</h1>
            <p>Weapon of choice: {turtle.weapon}</p>
            <img src={turtle.imgUrl} alt={`${turtle.name}`} width="200"/>
            <hr/>
        </div>
        )}
    </div>
)

Adding key to mapped elements

If you've followed along and checked the dev tools, you may have noticed an error React threw:

React wants to be able to distinguish each item in an array of JSX elements from other items. This can be especially important if you happened to have 2 times in an array that were exactly the same. This way, if a change is made to one of the items, React can know exactly which one to update.

So mapped elements expect you to add a property called key to the element being repeated. You can choose anything that will help uniquely identify the element. One easy way to do that is to add the index of the item in the array and some piece data from the element to the element as the key:

...

{turtles.map((turtle, index) =>
<div key={turtle.name + "-" + index}>

...

If you're wondering why the key is not just the index click here.

The only changes made here are parentheses around the arrow function's parameters (since I added a second one, parentheses are now required), the new index parameter in the function, and the key property to the root element I'm returning inside the .map().

Here's the whole React component so far:

function App() {
    const turtles = [
        {
            name: "Leonardo",
            nickName: "Leo",
            weapon: "Katana",
            imgUrl: "https://upload.wikimedia.org/wikipedia/en/e/ed/Leonardo_%28Teenage_Mutant_Ninja_Turtles%29.jpg"
        },
        {
            name: "Donatello",
            nickName: "Don",
            weapon: "Bo staff",
            imgUrl: "https://upload.wikimedia.org/wikipedia/en/5/5a/Donatello_%28Teenage_Mutant_Ninja_Turtles%29.jpg"
        },
        {
            name: "Michelangelo",
            nickName: "Mikey",
            weapon: "Nunchucks",
            imgUrl: "https://upload.wikimedia.org/wikipedia/en/f/f3/Michelangelo_%28Teenage_Mutant_Ninja_Turtles%29.jpg"
        },
        {
            name: "Raphael",
            nickName: "Raph",
            weapon: "Sai",
            imgUrl: "https://upload.wikimedia.org/wikipedia/en/7/72/Raphael_%28Teenage_Mutant_Ninja_Tutles%29.jpg"
        }
    ]

    return (
        <div>
            {turtles.map((turtle, index) =>
            <div key={turtle.name + index}>
                <h1>{turtle.name} ({turtle.nickName})</h1>
                <p>Weapon of choice: {turtle.weapon}</p>
                <img src={turtle.imgUrl} alt={`${turtle.name}`} width="200"/>
                <hr/>
            </div>
            )}
        </div>
    )
}

For a cleaner look, you can save the mapped array to a variable and just insert the variable like this.


    // Don't forget to return the array that .map creates!
    const displayTurtles = turtles.map((turtle, index) =>
        <div key={turtle.name + index}>
            <h1>{turtle.name} ({turtle.nickName})</h1>
            <p>Weapon of choice: {turtle.weapon}</p>
            <img src={turtle.imgUrl} alt={`${turtle.name}`} width="200"/>
            <hr/>
        </div>
    

return (
    <div>
        {displayTurtles}
    </div>
)

Once again, here's the final, completed component:

function App() {
    const turtles = [
        {
            name: "Leonardo",
            nickName: "Leo",
            weapon: "Katana",
            imgUrl: "https://upload.wikimedia.org/wikipedia/en/e/ed/Leonardo_%28Teenage_Mutant_Ninja_Turtles%29.jpg"
        },
        {
            name: "Donatello",
            nickName: "Don",
            weapon: "Bo staff",
            imgUrl: "https://upload.wikimedia.org/wikipedia/en/5/5a/Donatello_%28Teenage_Mutant_Ninja_Turtles%29.jpg"
        },
        {
            name: "Michelangelo",
            nickName: "Mikey",
            weapon: "Nunchucks",
            imgUrl: "https://upload.wikimedia.org/wikipedia/en/f/f3/Michelangelo_%28Teenage_Mutant_Ninja_Turtles%29.jpg"
        },
        {
            name: "Raphael",
            nickName: "Raph",
            weapon: "Sai",
            imgUrl: "https://upload.wikimedia.org/wikipedia/en/7/72/Raphael_%28Teenage_Mutant_Ninja_Tutles%29.jpg"
        }
    ]

    

    
    const displayTurtles = turtles.map((turtle, index) =>
        <div key={turtle.name + index}>
            <h1>{turtle.name} ({turtle.nickName})</h1>
            <p>Weapon of choice: {turtle.weapon}</p>
            <img src={turtle.imgUrl} alt={`${turtle.name}`} width="200"/>
            <hr/>
        </div>
        
    }
  
    return (
        <div>
            {displayTurtles}
        </div>
    )
}

Conclusion

If you're coming from the world of plain JS to create elements, this is an easier alternative than having to use a for loop and create elements manually with tools like document.createElement() and .appendChild().

If you're coming from the world of Angular 1, using JavaScript's native .map() is the React equivalent to using Angular's ng-repeat property.

You can start to see why people say that using React makes you a better JavaScript developer!