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
andmap
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
- Remove the key property on the
FruitListItem
as shown below:
const fruitListItems = props.fruits.map(fruit => (
<FruitListItem
- key={fruit.id}
fruit={fruit} />
));
- Refresh the page and check the console
- 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
- Add the key back
const fruitListItems = props.fruits.map(fruit => (
<FruitListItem
+ key={fruit.id}
fruit={fruit} />
));
- 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} />
);