Quickstart
A quick dive into getting started with Lore
A quick dive into getting started with Lore
In this step we're going to create a Tweet
component and make our tweets look more attractive.
You can view the finished code for this step by checking out the
data.2
branch of the completed project.
First we need to create the Tweet
component. Run this command to have the CLI generate it for us:
lore generate component Tweet
Then update the code for that component to look like this:
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
export default createReactClass({
displayName: 'Tweet',
propTypes: {
tweet: PropTypes.object.isRequired
},
render() {
const { tweet } = this.props;
return (
<li className="list-group-item tweet">
<div className="image-container">
<img
className="img-circle avatar"
src={'http://ssl.gstatic.com/images/icons/material/product/1x/avatar_circle_blue_120dp.png'} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
Nickname
</h4>
<h4 className="list-group-item-heading timestamp">
{'- Timestamp'}
</h4>
<p className="list-group-item-text text">
{'This is a quote from Chrono Trigger.'}
</p>
</div>
</li>
);
}
});
import React from 'react';
import PropTypes from 'prop-types';
class Tweet extends React.Component {
render() {
const { tweet } = this.props;
return (
<li className="list-group-item tweet">
<div className="image-container">
<img
className="img-circle avatar"
src={'http://ssl.gstatic.com/images/icons/material/product/1x/avatar_circle_blue_120dp.png'} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
Nickname
</h4>
<h4 className="list-group-item-heading timestamp">
{'- Timestamp'}
</h4>
<p className="list-group-item-text text">
{'This is a quote from Chrono Trigger.'}
</p>
</div>
</li>
);
}
}
Tweet.propTypes = {
tweet: PropTypes.object.isRequired
};
export default Tweet;
import React from 'react';
import PropTypes from 'prop-types';
class Tweet extends React.Component {
static propTypes = {
tweet: PropTypes.object.isRequired
};
render() {
const { tweet } = this.props;
return (
<li className="list-group-item tweet">
<div className="image-container">
<img
className="img-circle avatar"
src={'http://ssl.gstatic.com/images/icons/material/product/1x/avatar_circle_blue_120dp.png'} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
Nickname
</h4>
<h4 className="list-group-item-heading timestamp">
{'- Timestamp'}
</h4>
<p className="list-group-item-text text">
{'This is a quote from Chrono Trigger.'}
</p>
</div>
</li>
);
}
}
export default Tweet;
Here we've added a propType
declaring that this component expects to receive a tweet
, and then we've updated the render()
method to display it.
With our Tweet
component created, let’s use it in our Feed. Open the Feed
component and update the renderTweet()
method to look like this:
// src/components/Feed.js
import Tweet from './Tweet';
...
renderTweet(tweet) {
return (
<Tweet key={tweet.id} tweet={tweet} />
);
},
...
// src/components/Feed.js
import Tweet from './Tweet';
...
renderTweet(tweet) {
return (
<Tweet key={tweet.id} tweet={tweet} />
);
}
...
// src/components/Feed.js
import Tweet from './Tweet';
...
renderTweet(tweet) {
return (
<Tweet key={tweet.id} tweet={tweet} />
);
}
...
Refresh the browser and your app should now look like this:
While the Tweet
component is now showing up in the Feed
, it's currently showing some hard-coded data instead of the mock data we created previously. Let's change that.
Update the render()
method of the Tweet
component to look like this:
// src/components/Tweet.js
...
render() {
const { tweet } = this.props;
return (
<li className="list-group-item tweet">
<div className="image-container">
<img
className="img-circle avatar"
src={'http://ssl.gstatic.com/images/icons/material/product/1x/avatar_circle_blue_120dp.png'} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
Nickname
</h4>
<h4 className="list-group-item-heading timestamp">
{'- ' + tweet.data.createdAt}
</h4>
<p className="list-group-item-text text">
{tweet.data.text}
</p>
</div>
</li>
);
}
...
Refresh the browser and you should now see the mock data being displayed in the Tweet:
That's a little better, but what's up with that ugly timestamp of 2018-04-24T05:10:49.382Z
? That's not what we want; we want a clear statement like 3 days
to show how old the Tweet is. Luckily we can easily fix that using a library called moment.
Moment is a date/time library for Javascript, and it's a great tool for converting timestamps to a more human-friendly format.
Install moment
with this command:
npm install moment --save
After installing
moment
you may need to stop and restart the webpack development server in order for Webpack to see the new package.
Once moment
is installed, import it into your Tweet
component and update the render()
method to look like this:
// src/components/Tweet.js
import moment from 'moment';
...
render() {
const { tweet } = 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={'http://ssl.gstatic.com/images/icons/material/product/1x/avatar_circle_blue_120dp.png'} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
Nickname
</h4>
<h4 className="list-group-item-heading timestamp">
{'- ' + timestamp}
</h4>
<p className="list-group-item-text text">
{tweet.data.text}
</p>
</div>
</li>
);
}
...
Let's break down the statement moment(tweet.data.createdAt).fromNow().split(' ago')[0]
to explain what's happening.
First, we're calling moment(tweet.data.createdAt)
to convert the createdAt
date into a moment
object, which gives us access to utility methods.
Then we're appending fromNow()
to the end of that statement to convert the timestamp from the format 2018-04-24T05:10:49.382Z
into a human-readable string like 3 days ago
.
And finally, while we could leave it at that, the word "ago" in the phrase "3 days ago" is understood from the context. So we're also going to remove it from the final timestamp by splitting the string at the " ago" part and only taking the first piece (converting "3 days ago" to simply "3 days").
If everything went well, your application should now look like this.
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 moment from 'moment';
export default createReactClass({
displayName: 'Tweet',
propTypes: {
tweet: PropTypes.object.isRequired
},
render() {
const { tweet } = 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={'http://ssl.gstatic.com/images/icons/material/product/1x/avatar_circle_blue_120dp.png'} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
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';
class Tweet extends React.Component {
render() {
const { tweet } = 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={'http://ssl.gstatic.com/images/icons/material/product/1x/avatar_circle_blue_120dp.png'} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
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
};
export default Tweet;
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
class Tweet extends React.Component {
static propTypes = {
tweet: PropTypes.object.isRequired
};
render() {
const { tweet } = 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={'http://ssl.gstatic.com/images/icons/material/product/1x/avatar_circle_blue_120dp.png'} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
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;
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import Tweet from './Tweet';
export default 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 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 Feed;
import React from 'react';
import PropTypes from 'prop-types';
import Tweet from './Tweet';
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 we're going to add some mock user data to finish the Tweet component.