🔥 0
0
Lesson 7 of 9 15 min +75 XP

Lists and Keys

Displaying lists of data is one of the most common tasks in React. Whether it's a todo list, a product catalog, or a feed of posts, you'll need to transform arrays into elements.

Rendering Lists with map()

The map() method transforms each item in an array into a React element:

function FruitList() {
  const fruits = ['Apple', 'Banana', 'Orange'];

  return (
    <ul>
      {fruits.map(fruit => (
        <li key={fruit}>{fruit}</li>
      ))}
    </ul>
  );
}

Why Keys Matter

Keys help React identify which items have changed:

// Without keys, React can't tell items apart
// This causes bugs and poor performance

// ✅ With keys, React can efficiently update
<ul>
  {items.map(item => (
    <li key={item.id}>{item.name}</li>
  ))}
</ul>

What Happens Without Keys?

React will:

  • Re-render the entire list unnecessarily
  • Lose component state (inputs, scroll position)
  • Show warnings in the console

Choosing Good Keys

Best: Unique IDs from Your Data

// Data from an API usually has IDs
const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Charlie' }
];

{users.map(user => (
  <UserCard key={user.id} user={user} />
))}

Acceptable: Generated IDs

// If your data doesn't have IDs
const todos = [
  { text: 'Learn React', id: crypto.randomUUID() },
  { text: 'Build app', id: crypto.randomUUID() }
];

Avoid: Array Index

// ❌ Problematic when items can be reordered
{items.map((item, index) => (
  <li key={index}>{item}</li>
))}

Using index as key causes issues when:

  • Items can be reordered, inserted, or deleted
  • Items have state (like input fields)

Rendering Complex Lists

Objects with Multiple Properties

function ProductList({ products }) {
  return (
    <div className="grid">
      {products.map(product => (
        <div key={product.id} className="card">
          <img src={product.image} alt={product.name} />
          <h3>{product.name}</h3>
          <p>${product.price}</p>
          <button>Add to Cart</button>
        </div>
      ))}
    </div>
  );
}

Extracting List Item Components

function TodoItem({ todo, onToggle, onDelete }) {
  return (
    <li className={todo.done ? 'completed' : ''}>
      <input
        type="checkbox"
        checked={todo.done}
        onChange={() => onToggle(todo.id)}
      />
      <span>{todo.text}</span>
      <button onClick={() => onDelete(todo.id)}>Delete</button>
    </li>
  );
}

function TodoList({ todos, onToggle, onDelete }) {
  return (
    <ul>
      {todos.map(todo => (
        <TodoItem
          key={todo.id}
          todo={todo}
          onToggle={onToggle}
          onDelete={onDelete}
        />
      ))}
    </ul>
  );
}

Filtering and Sorting

Filtered Lists

function SearchResults({ items, query }) {
  const filtered = items.filter(item =>
    item.name.toLowerCase().includes(query.toLowerCase())
  );

  return (
    <ul>
      {filtered.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

Sorted Lists

function SortedList({ items, sortBy }) {
  const sorted = [...items].sort((a, b) => {
    if (sortBy === 'name') return a.name.localeCompare(b.name);
    if (sortBy === 'price') return a.price - b.price;
    return 0;
  });

  return (
    <ul>
      {sorted.map(item => (
        <li key={item.id}>{item.name} - ${item.price}</li>
      ))}
    </ul>
  );
}

Empty States

Always handle empty lists:

function MessageList({ messages }) {
  if (messages.length === 0) {
    return <p className="empty">No messages yet.</p>;
  }

  return (
    <ul>
      {messages.map(msg => (
        <li key={msg.id}>{msg.text}</li>
      ))}
    </ul>
  );
}

Nested Lists

function CategoryList({ categories }) {
  return (
    <div>
      {categories.map(category => (
        <div key={category.id}>
          <h2>{category.name}</h2>
          <ul>
            {category.items.map(item => (
              <li key={item.id}>{item.name}</li>
            ))}
          </ul>
        </div>
      ))}
    </div>
  );
}

Key Placement Rules

Keys must be on the outermost element returned from map:

// ❌ Wrong - key on inner element
{items.map(item => (
  <div>
    <span key={item.id}>{item.name}</span>
  </div>
))}

// ✅ Correct - key on outer element
{items.map(item => (
  <div key={item.id}>
    <span>{item.name}</span>
  </div>
))}

Key Takeaways

  • Use map() to transform arrays into React elements
  • Always provide a unique key prop for list items
  • Keys help React track items efficiently
  • Use stable IDs from your data, not array indices
  • Keys only need to be unique among siblings
  • Handle empty lists with appropriate UI

🧠 Quick Quiz

Test your understanding of this lesson.

1

Which array method is commonly used to render lists in React?

2

Why are keys important in React lists?

3

What makes a good key for list items?

useEffect and Side Effects