This post is part of the series 30 Days of React.

In this series, we're starting from the very basics and walk through everything you need to know to get started with React. If you've ever wanted to learn React, this is the place to start!

Data-Driven

Edit this page on Github

Hard-coding data in our applications isn't exactly ideal. Today, we'll set up our components to be driven by data to them access to external data.

Through this point, we've written our first components and set them up in a child/parent relationship. However, we haven't yet tied any data to our React components. Although it's a more pleasant experience (in our opinion) writing a website in React, we haven't taken advantage of the power of React to display any dynamic data.

Let's change that today.

Going data-driven

Recall, yesterday we built the beginning of our timeline component that includes a header and an activity list:

We broke down our demo into components and ended up building three separate components with static JSX templates. It's not very convenient to have to update our component's template everytime we have a change in our website's data.

Instead, let's give the components data to use to display. Let's start with the <Header /> component. As it stands right now, the <Header /> component only shows the title of the element as Timeline. It's a nice element and it would be nice to be able to reuse it in other parts of our page, but the title of Timeline doesn't make sense for every use.

Let's tell React that we want to be able to set the title to something else.

Introducing props

React allows us to send data to a component in the same syntax as HTML, using attributes or properties on a component. This is akin to passing the src attribute to an image tag. We can think about the property of the <img /> tag as a prop we're setting on a component called img.

We can access these properties inside a component as this.props. Let's see props in action.

Recall, we defined the <Header /> component as:

class Header extends React.Component {
  render() {
    return (
      <div className="header">
        <div className="menuIcon">
          <div className="dashTop"></div>
          <div className="dashBottom"></div>
          <div className="circle"></div>
        </div>
        <span className="title">Timeline</span>

        <input
          type="text"
          className="searchInput"
          placeholder="Search ..." />

        <div className="fa fa-search searchIcon"></div>
      </div>
    )
  }
}

When we use the <Header /> component, we placed it in our <App /> component as like so:

<Header />

We can pass in our title as a prop as an attribute on the <Header /> by updating the usage of the component setting the attribute called title to some string, like so:

<Header title="Timeline" />

Inside of our component, we can access this title prop from the this.props property in the Header class. Instead of setting the title statically as Timeline in the template, we can replace it with the property passed in.

class Header extends React.Component {
  render() {
    return (
      <div className="header">
        <div className="menuIcon">
          <div className="dashTop"></div>
          <div className="dashBottom"></div>
          <div className="circle"></div>
        </div>

        <span className="title">
          {this.props.title}
        </span>

        <input
          type="text"
          className="searchInput"
          placeholder="Search ..." />

        <div className="fa fa-search searchIcon"></div>
      </div>
    )
  }
}

We've also updated the code slightly to get closer to what our final <Header /> code will look like, including adding a searchIcon and a few elements to style the menuIcon.

Now our <Header /> component will display the string we pass in as the title when we call the component. For instance, calling our <Header /> component four times like so:

<Header title="Timeline" />
<Header title="Profile" />
<Header title="Settings" />
<Header title="Chat" />

Results in four <Header /> components to mount like so:

Pretty nifty, ey? Now we can reuse the <Header /> component with a dynamic title property.

We can pass in more than just strings in a component. We can pass in numbers, strings, all sorts of objects, and even functions! We'll talk more about how to define these different properties so we can build a component api later.

Instead of statically setting the content and date let's take the Content component and set the timeline content by a data variable instead of by text. Just like we can do with HTML components, we can pass multiple props into a component.

Recall, yesterday we defined our Content container like this:

class Content extends React.Component {
  render() {
    return (
      <div className="content">
        <div className="line"></div>

      {/* Timeline item */}
        <div className="item">
          <div className="avatar">
            <img src="http://www.croop.cl/UI/twitter/images/doug.jpg" />
            Doug
          </div>

          <span className="time">
            An hour ago
          </span>
          <p>Ate lunch</p>
          <div className="commentCount">
            2
          </div>
        </div>

        {/* ... */}

      </div>
    )
  }
}

As we did with title, let's look at what props our Content component needs:

  • A user's avatar image
  • A timestamp of the activity
  • Text of the activity item
  • Number of comments

Let's say that we have a JavaScript object that represents an activity item. We will have a few fields, such as a string field (text) and a date object. We might have some nested objects, like a user and comments. For instance:

{
  timestamp: new Date().getTime(),
  text: "Ate lunch",
  user: {
    id: 1,
    name: 'Nate',
    avatar: "http://www.croop.cl/UI/twitter/images/doug.jpg"
  },
  comments: [
    { from: 'Ari', text: 'Me too!' }
  ]
}

Just like we passed in a string title to the <Header /> component, we can take this activity object and pass it right into the Content component. Let's convert our component to display the details from this activity inside it's template.

In order to pass a dynamic variable's value into a template, we have to use the template syntax to render it in our template. For instance:

class Content extends React.Component {
  render() {
    const {activity} = this.props; // ES6 destructuring

    return (
      <div className="content">
        <div className="line"></div>

        {/* Timeline item */}
        <div className="item">
          <div className="avatar">
            <img
              alt={activity.text}
              src={activity.user.avatar} />
            {activity.user.name}
          </div>

          <span className="time">
            {activity.timestamp}
          </span>
          <p>{activity.text}</p>
          <div className="commentCount">
            {activity.comments.length}
          </div>
        </div>
      </div>
    )
  }
}

We've use a little bit of ES6 in our class definition on the first line of the render() function called destructuring. The two following lines are functionally equivalent:

// these lines do the same thing
const activity = this.props.activity;
const {activity} = this.props;

Destructuring allows us to save on typing and define variables in a shorter, more compact way.

We can then use this new content by passing in an object as a prop instead of a hard-coded string. For instance:

<Content activity={moment1} />

Fantastic, now we have our activity item driven by an object. However, you might have noticed that we would have to implement this multiple times with different comments. Instead, we could pass an array of objects into a component.

Let's say we have an object that contains multiple activity items:

const activities = [
  {
    timestamp: new Date().getTime(),
    text: "Ate lunch",
    user: {
      id: 1, name: 'Nate',
      avatar: "http://www.croop.cl/UI/twitter/images/doug.jpg"
    },
    comments: [{ from: 'Ari', text: 'Me too!' }]
  },
  {
    timestamp: new Date().getTime(),
    text: "Woke up early for a beautiful run",
    user: {
      id: 2, name: 'Ari',
      avatar: "http://www.croop.cl/UI/twitter/images/doug.jpg"
    },
    comments: [{ from: 'Nate', text: 'I am so jealous' }]
  },
]

We can rearticulate our usage of <Content /> by passing in multiple activities instead of just one:

<Content activities={activities} />

However, if we refresh the view nothing will show up! We need to first update our Content component to accept multiple activities. As we learned about previously, JSX is really just JavaScript executed by the browser. We can execute JavaScript functions inside the JSX content as it will just get run by the browser like the rest of our JavaScript.

Let's move our activity item JSX inside of the function of the map function that we'll run over for every item.

class Content extends React.Component {
  render() {
    const {activities} = this.props; // ES6 destructuring

    return (
      <div className="content">
        <div className="line"></div>

        {/* Timeline item */}
        {activities.map((activity) => {
          return (
            <div className="item">
              <div className="avatar">
                <img
                  alt={activity.text}
                  src={activity.user.avatar} />
                {activity.user.name}
              </div>

              <span className="time">
                {activity.timestamp}
              </span>
              <p>{activity.text}</p>
              <div className="commentCount">
                {activity.comments.length}
              </div>
            </div>
          );
        })}

      </div>
    )
  }
}

Now we can pass any number of activities to our array and the Content component will handle it, however if we leave the component right now, then we'll have a relatively complex component handling both containing and displaying a list of activities. Leaving it like this really isn't the React way.

ActivityItem

Here is where it makes sense to write one more component to contain displaying a single activity item and then rather than building a complex Content component, we can move the responsibility. This will also make it easier to test, add functionality, etc.

Let's update our Content component to display a list of ActivityItem components (we'll create this next).

class Content extends React.Component {
  render() {
    const {activities} = this.props; // ES6 destructuring

    return (
      <div className="content">
        <div className="line"></div>

        {/* Timeline item */}
        {activities.map((activity) => (
          <ActivityItem
            activity={activity} />
        ))}

      </div>
    )
  }
}

Not only is this much simpler and easier to understand, but it makes testing both components easier.

With our freshly-minted Content component, let's create the ActivityItem component. Since we already have the view created for the ActivityItem, all we need to do is copy it from what was our Content component's template as it's own module.

class ActivityItem extends React.Component {
  render() {
    const {activity} = this.props; // ES6 destructuring

    return (
      <div className="item">
        <div className="avatar">
          <img
            alt={activity.text}
            src={activity.user.avatar} />
          {activity.user.name}
        </div>

        <span className="time">
          {activity.timestamp}
        </span>
        <p>{activity.text}</p>
        <div className="commentCount">
          {activity.comments.length}
        </div>
      </div>
    )
  }
}

This week we updated our components to be driven by data by using the React props concept. In the next section, we'll dive into stateful components.

Learn React the right way

The up-to-date, in-depth, complete guide to React and friends.

Download the first chapter

Ari Lerner

Hi, I'm Ari. I'm an author of Fullstack React and ng-book and I've been teaching Web Development for a long time. I like to speak at conferences and eat spicy food. I technically got paid while I traveled the country as a professional comedian, but have come to terms with the fact that I am not funny.

Connect with Ari on Twitter at @auser.