Quickstart
A quick dive into getting started with Lore
A quick dive into getting started with Lore
In this step we're going to add a "delete" link to tweets that, when clicked, will launch a dialog to delete the tweet.
You can view the finished code for this step by checking out the
dialogs.7
branch of the completed project.
The dialog we'll create in this step will allow us to delete a tweet, and we'll be invoking the destroy
action to do that. Assuming you have a tweet
you want to delete, you invoke the action like this:
lore.actions.tweet.destroy(tweet)
The argument is the tweet
you want to destroy.
If we assume the id
of this tweet is 1
, then invoking this action will send a DELETE request to http://localhost:1337/tweets/1
.
You can learn more about the
destroy
action here.
Run this command to create a component for our delete link:
lore generate component DeleteLink
Then update the component to look like this:
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
export default createReactClass({
displayName: 'DeleteLink',
propTypes: {
tweet: PropTypes.object.isRequired
},
onClick() {
const { tweet } = this.props;
lore.dialog.show(function() {
return lore.dialogs.tweet.destroy(tweet, {
blueprint: 'optimistic',
request: function(data) {
return lore.actions.tweet.destroy(tweet).payload;
}
});
});
},
render() {
return (
<a className="link" onClick={this.onClick}>
delete
</a>
);
}
});
import React from 'react';
import PropTypes from 'prop-types';
class DeleteLink extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
const { tweet } = this.props;
lore.dialog.show(function() {
return lore.dialogs.tweet.destroy(tweet, {
blueprint: 'optimistic',
request: function(data) {
return lore.actions.tweet.destroy(tweet).payload;
}
});
});
}
render() {
return (
<a className="link" onClick={this.onClick}>
delete
</a>
);
}
}
DeleteLink.propTypes = {
tweet: PropTypes.object.isRequired
};
export default DeleteLink;
import React from 'react';
import PropTypes from 'prop-types';
class DeleteLink extends React.Component {
static propTypes = {
tweet: PropTypes.object.isRequired
};
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
const { tweet } = this.props;
lore.dialog.show(function() {
return lore.dialogs.tweet.destroy(tweet, {
blueprint: 'optimistic',
request: function(data) {
return lore.actions.tweet.destroy(tweet).payload;
}
});
});
}
render() {
return (
<a className="link" onClick={this.onClick}>
delete
</a>
);
}
}
export default DeleteLink;
In the code above, we're rendering a link with an onClick
callback. When clicked, we'll show a confirmation dialog, and then invoke the destroy
action when the request is confirmed.
Next we want to add the delete link to each tweet. Open the Tweet
component and update the render()
method to look like this:
// src/components/Tweet.js
...
import DeleteLink from './DeleteLink';
...
render() {
...
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 className="tweet-actions">
<EditLink tweet={tweet} />
<DeleteLink tweet={tweet} />
</div>
</div>
</li>
);
}
...
With this change in place, refresh the browser and you should see a "delete" link on each of the tweets.
If you click this link, you'll be asked to confirm that you want to delete the tweet. Once you confirm, if you look at the network requests, you'll see a DELETE request is sent to the API to delete the tweet.
The
state
of the tweet is also changed toDELETING
, so if this were a real application, we could add an if statement to detect when data was being changed and modify our UI to communicate that to the user.
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';
export default createReactClass({
displayName: 'DeleteLink',
propTypes: {
tweet: PropTypes.object.isRequired
},
onClick() {
const { tweet } = this.props;
lore.dialog.show(function() {
return lore.dialogs.tweet.destroy(tweet, {
blueprint: 'optimistic',
request: function(data) {
return lore.actions.tweet.destroy(tweet).payload;
}
});
});
},
render() {
return (
<a className="link" onClick={this.onClick}>
delete
</a>
);
}
});
import React from 'react';
import PropTypes from 'prop-types';
class DeleteLink extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
const { tweet } = this.props;
lore.dialog.show(function() {
return lore.dialogs.tweet.destroy(tweet, {
blueprint: 'optimistic',
request: function(data) {
return lore.actions.tweet.destroy(tweet).payload;
}
});
});
}
render() {
return (
<a className="link" onClick={this.onClick}>
delete
</a>
);
}
}
DeleteLink.propTypes = {
tweet: PropTypes.object.isRequired
};
export default DeleteLink;
import React from 'react';
import PropTypes from 'prop-types';
class DeleteLink extends React.Component {
static propTypes = {
tweet: PropTypes.object.isRequired
};
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
const { tweet } = this.props;
lore.dialog.show(function() {
return lore.dialogs.tweet.destroy(tweet, {
blueprint: 'optimistic',
request: function(data) {
return lore.actions.tweet.destroy(tweet).payload;
}
});
});
}
render() {
return (
<a className="link" onClick={this.onClick}>
delete
</a>
);
}
}
export default DeleteLink;
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';
import EditLink from './EditLink';
import DeleteLink from './DeleteLink';
export default connect(function(getState, props) {
const { tweet } = props;
return {
user: getState('user.byId', {
id: tweet.data.user
})
};
})(
createReactClass({
displayName: 'Tweet',
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 className="tweet-actions">
<EditLink tweet={tweet} />
<DeleteLink tweet={tweet} />
</div>
</div>
</li>
);
}
})
);
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { connect } from 'lore-hook-connect';
import EditLink from './EditLink';
import DeleteLink from './DeleteLink';
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 className="tweet-actions">
<EditLink tweet={tweet} />
<DeleteLink tweet={tweet} />
</div>
</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.user
})
};
})(Tweet);
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { connect } from 'lore-hook-connect';
import EditLink from './EditLink';
import DeleteLink from './DeleteLink';
@connect(function(getState, props) {
const tweet = props.tweet;
return {
user: getState('user.byId', {
id: tweet.data.user
})
};
})
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 className="tweet-actions">
<EditLink tweet={tweet} />
<DeleteLink tweet={tweet} />
</div>
</div>
</li>
);
}
}
export default Tweet;
In the next section we'll be hiding the edit and delete links to reflect the application's user permissions.