Quickstart

A quick dive into getting started with Lore

Step 5: Fetch User for Tweet

In this step we're going to fetch the user for each tweet, so that we can display the proper nickname and avatar.

You can view the finished code for this step by checking out the fetching branch of the completed project.

Create the User Model

Before we can fetch users, we need to create a model to represent the resource. Run this command to create a user model:

lore generate model user

This will place a file called user.js in src/models and, just like when you created the tweet model, you will now have access to a set of actions and reducers for interacting with the /users endpoint.

Fetch the Tweet's User

Next we need to fetch the user who created the tweet. Open the Tweet component and wrap it with connect, but this time we're going to use a different string in the getState() method:

// src/components/Tweet.js
...
import { connect } from 'lore-hook-connect';

export default connect(function(getState, props) {
  const { tweet } = props;

  return {
    user: getState('user.byId', {
      id: tweet.data.userId
    })
  };
})(
createReactClass({
  displayName: 'Tweet',

  ...
})
);
// src/components/Tweet.js
...
import { connect } from 'lore-hook-connect';

class Tweet extends React.Component {
  ...
}

export default connect(function(getState, props) {
  const tweet = props.tweet;

  return {
    user: getState('user.byId', {
      id: tweet.data.userId
    })
  };
})(Tweet);
// src/components/Tweet.js
...
import { connect } from 'lore-hook-connect';

@connect(function(getState, props) {
  const tweet = props.tweet;

  return {
    user: getState('user.byId', {
      id: tweet.data.userId
    })
  };
})
class Tweet extends React.Component {
  ...
}

The string we're passing to the getState() method this time is user.byId. But unlike the tweet.find call, this time we need to provide an argument; the id of the user you want to retrieve. If the user exists in the store, it will be returned immediately. If not, an action will be invoked to fetch that user from the API.

You can learn more about the byId blueprint here.

With this change in place, refresh the browser and you should see each tweet attributed to the correct user.

At this point both our Feed and Tweet components are fetching real data, which means you can safely delete the getDefaultProps() method from both components if you'd like. Seeing as we are no longer using them to insert mock data, they no longer serve a purpose.

Code Changes

Below is a list of files modified during this step.

src/models/user.js

export default {

};
export default {

}
export default {

}

src/components/Tweet.js

import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import moment from 'moment';
import { connect } from 'lore-hook-connect';

export default connect(function(getState, props) {
  const { tweet } = props;

  return {
    user: getState('user.byId', {
      id: tweet.data.userId
    })
  };
})(
createReactClass({
  displayName: 'Tweet',

  propTypes: {
    tweet: PropTypes.object.isRequired,
    user: PropTypes.object.isRequired
  },

  getDefaultProps() {
    return {
      user: {
        id: 1,
        data: {
          id: 1,
          nickname: "lucca",
          avatar: "https://cloud.githubusercontent.com/assets/2637399/19027072/a36f0c7a-88e1-11e6-931e-7f67fe01367b.png"
        }
      }
    };
  },

  render() {
    const { tweet, user } = this.props;
    const timestamp = moment(tweet.data.createdAt).fromNow().split(' ago')[0];

    return (
      <li className="list-group-item tweet">
        <div className="image-container">
          <img
            className="img-circle avatar"
            src={user.data.avatar} />
        </div>
        <div className="content-container">
          <h4 className="list-group-item-heading title">
            {user.data.nickname}
          </h4>
          <h4 className="list-group-item-heading timestamp">
            {'- ' + timestamp}
          </h4>
          <p className="list-group-item-text text">
            {tweet.data.text}
          </p>
        </div>
      </li>
    );
  }

})
);
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { connect } from 'lore-hook-connect';

class Tweet extends React.Component {

  render() {
    const { tweet, user } = this.props;
    const timestamp = moment(tweet.data.createdAt).fromNow().split(' ago')[0];

    return (
      <li className="list-group-item tweet">
        <div className="image-container">
          <img
            className="img-circle avatar"
            src={user.data.avatar} />
        </div>
        <div className="content-container">
          <h4 className="list-group-item-heading title">
            {user.data.nickname}
          </h4>
          <h4 className="list-group-item-heading timestamp">
            {'- ' + timestamp}
          </h4>
          <p className="list-group-item-text text">
            {tweet.data.text}
          </p>
        </div>
      </li>
    );
  }

}

Tweet.propTypes = {
  tweet: PropTypes.object.isRequired,
  user: PropTypes.object.isRequired
};

export default connect(function(getState, props) {
  const tweet = props.tweet;

  return {
    user: getState('user.byId', {
      id: tweet.data.userId
    })
  };
})(Tweet);
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { connect } from 'lore-hook-connect';

@connect(function(getState, props) {
  const tweet = props.tweet;

  return {
    user: getState('user.byId', {
      id: tweet.data.userId
    })
  };
})
class Tweet extends React.Component {

  static propTypes = {
    tweet: PropTypes.object.isRequired,
    user: PropTypes.object.isRequired
  };

  render() {
    const { tweet, user } = this.props;
    const timestamp = moment(tweet.data.createdAt).fromNow().split(' ago')[0];

    return (
      <li className="list-group-item tweet">
        <div className="image-container">
          <img
            className="img-circle avatar"
            src={user.data.avatar} />
        </div>
        <div className="content-container">
          <h4 className="list-group-item-heading title">
            {user.data.nickname}
          </h4>
          <h4 className="list-group-item-heading timestamp">
            {'- ' + timestamp}
          </h4>
          <p className="list-group-item-text text">
            {tweet.data.text}
          </p>
        </div>
      </li>
    );
  }

}

export default Tweet;

Next Steps

Next we're going to learn how to implement an authentication flow.