Quickstart
A quick dive into getting started with Lore
A quick dive into getting started with Lore
In this step we're going to learn how to use the state
property in the data structure, and display a loading experience while the list of tweets are being fetched.
You can view the finished code for this step by checking out the
fetching.4
branch of the completed project.
While it may happen too quickly to notice, there is a period of time before the list of tweets is displayed when the page simply says "Feed" with nothing underneath.
This happens because the application doesn't fetch the tweets until the first time the Feed
component is rendered, and during that render cycle, the component doesn't have any data to render.
Showing a blank view is a bad user experience, so let's update our Feed
component to display a "loader" while the tweets are being fetched.
When Lore performs any actions on data (such as fetching, creating, updating or deleting it) the state
property of that data is updated to reflect the action being performed.
For example, the first time our Feed
component is rendered, it requests a collection of tweets using getState('tweet.find')
. Since this data doesn't exist in the local store yet, the framework will invoke an action to fetch it.
What gets provided to the Feed
component at that point is a tweets
prop that looks like this:
{
state: 'FETCHING',
data: [],
query: {}
}
Here the state
property of the data has been set to FETCHING
in order to notify you that the data is being fetched. Once the data returns, the state
property will have a value of RESOLVED
to let you know that the data has been fetched. And if an error occurs while fetching the data, the state
property would be updated to ERROR_FETCHING
.
To make it easier to refer to these states, there's a file located at src/constants/PayloadStates.js
that contains all the states Lore might assign to data. If you open it, there will a commented out section that shows the default states used by the framework, which are these:
export default {
INITIAL_STATE: 'INITIAL_STATE',
RESOLVED : 'RESOLVED',
NOT_FOUND: 'NOT_FOUND',
DELETED: 'DELETED',
CREATING: 'CREATING',
UPDATING: 'UPDATING',
DELETING: 'DELETING',
FETCHING: 'FETCHING',
ERROR_CREATING: 'ERROR_CREATING',
ERROR_UPDATING: 'ERROR_UPDATING',
ERROR_DELETING: 'ERROR_DELETING',
ERROR_FETCHING: 'ERROR_FETCHING'
};
Let's use this file to create our loading experience.
You can learn more about this file here.
Open the Feed
component and import PayloadStates
. Then update the render()
method to display a loading experience when the tweets are being fetched, like this:
// src/components/Feed.js
...
import PayloadStates from '../constants/PayloadStates';
...
render() {
const { tweets } = this.props;
if (tweets.state === PayloadStates.FETCHING) {
return (
<div className="feed">
<h2 className="title">
Feed
</h2>
<div className="loader"/>
</div>
);
}
return (
<div className="feed">
<h2 className="title">
Feed
</h2>
<ul className="media-list tweets">
{tweets.data.map(this.renderTweet)}
</ul>
</div>
);
}
...
Here we've added a check if the tweet.state
property is equal to PayloadStates.FETCHING
, and if it is, then we're rendering our loading experience.
Refresh the browser and you might see an animated "loader" flash on the screen right before the tweets are rendered. But if you can't see it (and you probably can't) don't worry; later we'll connect to a real API server that has an artificial delay, and you'll definitely see it then.
If everything went well, your application should now look like this when the Feed is loading:
Below is a list of files modified during this step.
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import { connect } from 'lore-hook-connect';
import PayloadStates from '../constants/PayloadStates';
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;
if (tweets.state === PayloadStates.FETCHING) {
return (
<div className="feed">
<h2 className="title">
Feed
</h2>
<div className="loader"/>
</div>
);
}
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 PayloadStates from '../constants/PayloadStates';
import Tweet from './Tweet';
class Feed extends React.Component {
renderTweet(tweet) {
return (
<Tweet key={tweet.id} tweet={tweet} />
);
}
render() {
const { tweets } = this.props;
if (tweets.state === PayloadStates.FETCHING) {
return (
<div className="feed">
<h2 className="title">
Feed
</h2>
<div className="loader"/>
</div>
);
}
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 PayloadStates from '../constants/PayloadStates';
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;
if (tweets.state === PayloadStates.FETCHING) {
return (
<div className="feed">
<h2 className="title">
Feed
</h2>
<div className="loader"/>
</div>
);
}
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 we're going to fetch the user for each tweet.