Skip to main content

Lists

Often the answer to the question of how do I do something in React can be answered by understanding how would you do it in JavaScript.

In Vanilla JavaScript

In JavaScript, the approach we use to loop through an Array and transform the data in it has evolved to a more functional style of programming with the addition of:

  • functions like forEach and map to the Array in ES5
  • the arrow function in ES6 (ES2015)

Clear the code in main.js and work through each example below to see the progression.

a. for loop

const numbers = [1, 2, 3, 4, 5];
const tens = [];

for (let index = 0; index < numbers.length; index++) {
const number = numbers[index];
tens.push(number * 10);
}

console.log(tens);

b. #array.forEach

const numbers = [1, 2, 3, 4, 5];
const tens = [];

numbers.forEach(function (number) {
tens.push(number * 10);
});

console.log(tens);

c. #array.map

const numbers = [1, 2, 3, 4, 5];

const tens = numbers.map(function (number) {
return number * 10;
});

console.log(tens);

d. #array.map with arrow function

const numbers = [1, 2, 3, 4, 5];
const tens = numbers.map((number) => number * 10);
console.log(tens);

In React: Rendering Multiple Elements

We can use the Array's map function to transform an array of data into an array of React elements and then render them as follows.

function FruitList(props) {
const fruitListItems = props.fruits.map((fruit) => (
<li key={fruit.id}>{fruit.name}</li>
));
return <ul>{fruitListItems}</ul>;
}

const data = [
{ id: 1, name: 'apple' },
{ id: 2, name: 'orange' },
{ id: 3, name: 'blueberry' },
{ id: 4, name: 'banana' },
{ id: 5, name: 'kiwi' },
];

ReactDOM.createRoot(document.getElementById('root')).render(
<FruitList fruits={data} />
);

Of course, you could do the above with more verbose code using a Array forEach or a for loop in JavaScript. I've included examples of each below. You will use mostly use the terser map but it might help your understanding to see the following equivalent code examples.

function FruitList(props) {
const fruitListItems = [];
const fruits = props.fruits;
for (let index = 0; index < fruits.length; index++) {
const fruit = fruits[index];
const fruitListItem = <li key={fruit.id}>{fruit.name}</li>;
fruitListItems.push(fruitListItem);
}
return <ul>{fruitListItems}</ul>;
}
function FruitList(props) {
const fruitListItems = [];
props.fruits.forEach((fruit) => {
const fruitListItem = <li key={fruit.id}>{fruit.name}</li>;
fruitListItems.push(fruitListItem);
});
return <ul>{fruitListItems}</ul>;
}

In React: Rendering Multiple Components

This same approach works with components as well.

In the example, below we extract each list item into an reusable component.

function FruitListItem(props) {
return <li>{props.fruit.name}</li>;
}

function FruitList(props) {
const fruitListItems = props.fruits.map((fruit) => (
<FruitListItem key={fruit.id} fruit={fruit} />
));
return <ul>{fruitListItems}</ul>;
}

const data = [
{ id: 1, name: 'apple' },
{ id: 2, name: 'orange' },
{ id: 3, name: 'blueberry' },
{ id: 4, name: 'banana' },
{ id: 5, name: 'kiwi' },
];

ReactDOM.createRoot(document.getElementById('root')).render(
<FruitList fruits={data} />
);

Keys

  1. Remove the key property on the FruitListItem as shown below:
  const fruitListItems = props.fruits.map(fruit => (
<FruitListItem
- key={fruit.id}
fruit={fruit} />
));
  1. Refresh the page and check the console
  2. You should see the following warning:
Warning: Each child in a list should have a unique "key" prop.

Remember React has a Virtual representation of the DOM and strives to do as few DOM operations as possible when re-rendering. Adding a unique key to the list items allows React to efficiently re-render the UI.

Adding Keys

  1. Add the key back
  const fruitListItems = props.fruits.map(fruit => (
<FruitListItem
+ key={fruit.id}
fruit={fruit} />
));
  1. Refresh the browser and the warning will no longer appear

The key need should be a stable unique identifier for the item in the list.

When you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort:

function FruitList(props) {
const fruitListItems = props.fruits.map((fruit, index) => (
<FruitListItem key={index} fruit={fruit} />
));
return <ul>{fruitListItems}</ul>;
}

It is not recommended to use indexes for keys if the order of items may change (add, remove, delete, or move items). If you choose not to assign an explicit key to list items then React will default to using indexes as keys.

Where to put Keys

Keys only make sense in the context of the surrounding array.

Keys should be just inside of the loop not encapsulated inside a child component.

function FruitListItem(props) {
const fruit = props.fruit;
return <li key={fruit.id}>{fruit.name}</li>;
}

function FruitList(props) {
const fruitListItems = props.fruits.map((fruit) => (
<FruitListItem fruit={fruit} />
));
return <ul>{fruitListItems}</ul>;
}

const data = [
{ id: 1, name: 'apple' },
{ id: 2, name: 'orange' },
{ id: 3, name: 'blueberry' },
{ id: 4, name: 'banana' },
{ id: 5, name: 'kiwi' },
];
ReactDOM.createRoot(document.getElementById('root')).render(
<FruitList fruits={data} />
);

This example will still give the warning:

 Warning: Each child in a list should have a unique "key" prop.

To remove the warning you will need to add the key closer to the loop as we did previously.

The complete code example is shown again below.

function FruitListItem(props) {
return <li>{props.fruit.name}</li>;
}

function FruitList(props) {
const fruitListItems = props.fruits.map((fruit) => (
<FruitListItem key={fruit.id} fruit={fruit} />
));
return <ul>{fruitListItems}</ul>;
}

const data = [
{ id: 1, name: 'apple' },
{ id: 2, name: 'orange' },
{ id: 3, name: 'blueberry' },
{ id: 4, name: 'banana' },
{ id: 5, name: 'kiwi' },
];

ReactDOM.createRoot(document.getElementById('root')).render(
<FruitList fruits={data} />
);

map() in JSX

When you are first learning to map an array into React elements or components assigning the resulting array into a variable as we have thus far can be easier to understand and read.

As you get more comfortable in JSX using map directly in the return statement in JSX as shown below can be useful.

function FruitListItem(props) {
return <li>{props.fruit.name}</li>;
}

function FruitList(props) {
return (
<ul>
{props.fruits.map((fruit) => (
<FruitListItem key={fruit.id} fruit={fruit} />
))}
</ul>
);
}

const data = [
{ id: 1, name: 'apple' },
{ id: 2, name: 'orange' },
{ id: 3, name: 'blueberry' },
{ id: 4, name: 'banana' },
{ id: 5, name: 'kiwi' },
];

ReactDOM.createRoot(document.getElementById('root')).render(
<FruitList fruits={data} />
);

Reference