Rendering Dynamic Data with Components

Course: React JS & Tailwind CSS - Full Course

Introduction

In real-world apps, you rarely know ahead of time exactly how many items you’ll need to render. Whether it’s tasks in a todo app, products in a store, or posts in a blog, your data usually comes from a database or an API.

That means hardcoding each component with individual props quickly becomes unmanageable. Instead, React allows us to take an array of objects and dynamically render components for each item. This keeps your code clean, organized, and ready to handle any amount of data.

In this section, we’ll explore how to take a list of data and turn it into reusable components using .map(). By the end, you’ll understand the pattern that’s used in almost every React app and get a hands-on challenge to practice it with a TaskItem component for your task tracker app.

Mapping Data into Components

In the previous lesson, we created a reusable UserCard component but we were still manually passing information into those cards.

This works fine for a few users, but it’s not scalable. What if you have 100 users coming from a database? You wouldn’t want to manually write a <UserCard> for each one.

Instead, you can store your data in an array of objects (like a database) and use .map() to render your components dynamically (this is where knowing JavaScript fundamentals comes in handy):

function App() {
  const users = [
    { id: 1, name: "Alice Johnson", email: "alice@example.com", phone: "123-456-7890" },
    { id: 2, name: "Bob Smith", email: "bob@example.com", phone: "987-654-3210" },
  ];

  return (
    <div>
      {users.map(user => (
        <UserCard 
          name={user.name} 
          email={user.email} 
          phone={user.phone} 
        />
      ))}
    </div>
  );
}

As you can see, we use .map() to loop over the array and each UserCard gets its data from the object.

However, if you hit save you might see an error like this in your console: Warning: Each child in a list should have a unique "key" prop.

Although your component may still render this could lead to some unexpected behavior.

Why Keys are Necessary

When we render a list of components using .map(), React needs a way to identify each component uniquely. This is important for performance and correctness. If a component is added, removed, or changed, React uses these keys to decide which components to update in the DOM.

To address this, we'll need to include a key prop. It is important to note, keys need to be unique among siblings and stable (does not change).

The fix is quite simple, we just need to provide a unique "key" for each component. Most databases will have a unique identifier for their objects, and we can assign it to the key like so:

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

Challenge

Now it’s your turn!

Your task:

  • Create a new component called TaskItem in its own file.
  • The component should accept a task object as a prop and render:
    • The task text
    • A Mark Complete button
    • An Edit button
    • A Delete button
  • In App.jsx, create an array of task objects and map over it to render a TaskItem for each task.
  • Don’t forget the key prop for each TaskItem.

Note: You don’t need to add functionality to the buttons yet. This challenge is just about building reusable components and rendering them from data.

Try to write this yourself before looking at the solution!

Solution

First create the an array of task objects in your App.jsx:

const tasks = [
    { id: 1, text: "Buy groceries" },
    { id: 2, text: "Walk the dog" },
    { id: 3, text: "Finish React course" },
  ]

Then create a new component called TaskItem.jsx in the components folder:

function TaskItem({ task }) {
  return (
    <div key={task.id}>
      <p>{task.text}</p>
      <button>Mark Complete<button/>
      <button>Edit</button>
      <button>Delete</button>
    </div>
  )
}

export default TaskItem

After that, import your TaskItem component and map each task into TaskItem:

 {tasks.map((task) => (
        <TaskItem key={task.id} task={task} />
      ))}