Quickstart

A quick dive into getting started with Lore

Step 3: Connect Feed Component

In this step we're going to learn about the connect decorator, and use it to allow our Tweet component to declare what data it needs.

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

The Connect Decorator

Lore provides a decorator (also known as a Higher Order Component) that allows components to declare what data they need, and the framework will automatically retrieve it from the API if it doesn't exist in the local store.

That decorator is called connect and the syntax for using it looks like this:

connect(function(getState, props) {
  return {
    tweets: getState('tweet.find')
  };
})(createReactClass({...}));
class MyComponent extends React.Component {...}

connect(function(getState, props) {
  return {
    tweets: getState('tweet.find')
  };
})(MyComponent);
@connect(function(getState, props) {
  return {
    tweets: getState('tweet.find')
  };
})
class MyComponent extends React.Component {...}

You can learn more about connect here.

The first parameter, getState, is a function that will retrieve a piece of state from the local store, or invoke the appropriate action to retrieve that data if it hasn't been fetched yet.

The string you provide to getState() is referred to as a "blueprint", and describes what you want the function to do. The first part, tweet, is the resource you want to fetch, and the second part, find, is the name of the blueprint.

In this example we're providing tweet.find, and since we aren't passing in any query parameters or pagination information, this request translates to "make a call to the /tweets endpoint of the API and give me whatever comes back".

You can learn more about the find blueprint here.

The data returned will then be passed to the Feed component through a prop named tweets, since that's what we named the key.

Connect the Feed Component

To use connect, you first need to import it from the lore-hook-connect package, which is already included in your project (we'll introduce hooks later in this tutorial).

Open your Feed component and wrap it with the connect decorator like this:

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

export default connect(function(getState, props) {
  return {
    tweets: getState('tweet.find')
  };
})(
createReactClass({
  ...
})
);
// src/components/Feed.js
...
import { connect } from 'lore-hook-connect';

class Feed extends React.Component {
  ...
};

export default connect(function(getState, props) {
  return {
    tweets: getState('tweet.find')
  };
})(Feed);
// src/components/Feed.js
...
import { connect } from 'lore-hook-connect';

@connect(function(getState, props) {
  return {
    tweets: getState('tweet.find')
  };
})
class Feed extends React.Component {
  ...
};

export default Feed;

Once you've wrapped the Feed component, reload the page, and you'll notice the mock data has been replaced by real data from the API.

Visual Check-in

If everything went well, your application should now look like this.

Code Changes

Below is a list of files modified during this step.

src/components/Feed.js

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

export default connect(function(getState, props) {
  return {
    tweets: getState('tweet.find')
  };
})(
createReactClass({
  displayName: 'Feed',

  propTypes: {
    tweets: PropTypes.object.isRequired
  },

  getDefaultProps() {
    const tweet = {
      id: 1,
      cid: 'c1',
      state: 'RESOLVED',
      data: {
        id: 1,
        userId: 1,
        text: 'Nothing can beat science!',
        createdAt: '2018-04-24T05:10:49.382Z'
      }
    };

    return {
      tweets: {
        state: 'RESOLVED',
        data: [tweet]
      }
    };
  },

  renderTweet(tweet) {
    return (
      <Tweet key={tweet.id} tweet={tweet} />
    );
  },

  render() {
    const { tweets } = this.props;

    return (
      <div className="feed">
        <h2 className="title">
          Feed
        </h2>
        <ul className="media-list tweets">
          {tweets.data.map(this.renderTweet)}
        </ul>
      </div>
    );
  }

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

class Feed extends React.Component {

  renderTweet(tweet) {
    return (
      <Tweet key={tweet.id} tweet={tweet} />
    );
  }

  render() {
    const { tweets } = this.props;

    return (
      <div className="feed">
        <h2 className="title">
          Feed
        </h2>
        <ul className="media-list tweets">
          {tweets.data.map(this.renderTweet)}
        </ul>
      </div>
    );
  }

}

Feed.propTypes = {
  tweets: PropTypes.object.isRequired
};

Feed.defaultProps = (function() {
  const tweet = {
    id: 1,
    cid: 'c1',
    state: 'RESOLVED',
    data: {
      id: 1,
      userId: 1,
      text: 'Nothing can beat science!',
      createdAt: '2018-04-24T05:10:49.382Z'
    }
  };

  return {
    tweets: {
      state: 'RESOLVED',
      data: [tweet]
    }
  };
})();

export default connect(function(getState, props) {
  return {
    tweets: getState('tweet.find')
  };
})(Feed);
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'lore-hook-connect';
import Tweet from './Tweet';

@connect(function(getState, props) {
  return {
    tweets: getState('tweet.find')
  };
})
class Feed extends React.Component {

  static propTypes = {
    tweets: PropTypes.object.isRequired
  };

  static defaultProps = (function() {
    const tweet = {
      id: 1,
      cid: 'c1',
      state: 'RESOLVED',
      data: {
        id: 1,
        userId: 1,
        text: 'Nothing can beat science!',
        createdAt: '2018-04-24T05:10:49.382Z'
      }
    };

    return {
      tweets: {
        state: 'RESOLVED',
        data: [tweet]
      }
    };
  })();

  renderTweet(tweet) {
    return (
      <Tweet key={tweet.id} tweet={tweet} />
    );
  }

  render() {
    const { tweets } = this.props;

    return (
      <div className="feed">
        <h2 className="title">
          Feed
        </h2>
        <ul className="media-list tweets">
          {tweets.data.map(this.renderTweet)}
        </ul>
      </div>
    );
  }

}

export default Feed;

Next Steps

Next we're going to display a loading experience while the tweets are being fetched.