In this project, we'll complete the functionality of a social media platform by using axios
in a react
project. Majority of the application has already been built out for you. Take some time before starting to familarize yourself with the component structure. The platform we'll be building out is a scaled down version of twitter and we'll focus on the functionality for fetching all the DevMountain posts, creating new posts, editing existing posts, and deleting existing posts.
This is a live API, please be respectful and mindful of what you post.
Fork
this repository.Clone
your fork.cd
into the project directory.- Run
npm install
. - Run
npm start
.
Before continuing to step 1, take a minute to browse the API documentation.
https://swaggerhub.com/apis/DevMountain/social_mountain/1.0.0
In this step, we'll use axios
to fetch posts
from the API and render them on the DOM
.
- Add
axios
to the project usingnpm install axios
. - Open
./src/components/App.js
. - Import
axios
into the component. - Use
axios
and the API documentation to fetchposts
in thecomponentDidMount
method.- Set the
posts
array returned from the API ontoposts
onstate
.
- Set the
- Import the
Post
component. - Underneathe the
Compose
component, map overposts
onstate
and render aPost
component for eachpost
.- Remember that React requires a unique key property when using a
map
.
- Remember that React requires a unique key property when using a
Detailed Instructions
Let's begin by opening a terminal and cd
into the root of the project. We'll need to add axios
to our project in order to make API requests. We can do so by running npm install axios
.
Now let's open ./src/components/App.js
and import axios
into the component. Near the top of the file with the other import statements, add the following code:
import axios from 'axios';
Now that we have axios
imported, we can go into the componentDidMount
method and make a GET
request to the API. Using the API documentation we can see that we need to make a GET
request to https://practiceapi.devmountain.com/api/posts
. The API documentation also explains that a GET
request returns an array of post objects. We'll need to capture this returned array and set it onto posts
on App
's state
.
componentDidMount() {
axios.get('https://practiceapi.devmountain.com/api/posts').then( results => {
this.setState({ posts: results.data });
});
}
Now when the App
component mounts it will fetch the array of posts
. The last thing we'll need to do is map
over the posts
and render them onto the DOM
. We'll need to import the Post
component into ./src/components/App.js
. Once it has been imported, we can go into the render
method and render a Post
component for every post
in the posts
array on state
. Remember when using a map
in the render
method, the element needs a unique key
property. In this case, we can use the id
of the post
.
render() {
const { posts } = this.state;
return (
<div className="App__parent">
<Header />
<section className="App__content">
<Compose />
{
posts.map( post => (
<Post key={ post.id } />
))
}
</section>
</div>
);
}
./src/components/App.js
import React, { Component } from 'react';
import axios from 'axios';
import './App.css';
import Header from './Header/Header';
import Compose from './Compose/Compose';
import Post from './Post/Post';
class App extends Component {
constructor() {
super();
this.state = {
posts: []
};
this.updatePost = this.updatePost.bind( this );
this.deletePost = this.deletePost.bind( this );
this.createPost = this.createPost.bind( this );
}
componentDidMount() {
axios.get('https://practiceapi.devmountain.com/api/posts').then( results => {
this.setState({ posts: results.data });
});
}
updatePost() {
}
deletePost() {
}
createPost() {
}
render() {
const { posts } = this.state;
return (
<div className="App__parent">
<Header />
<section className="App__content">
<Compose />
{
posts.map( post => (
<Post key={ post.id } />
))
}
</section>
</div>
);
}
}
export default App;
In this step, we'll pass props
down into the Post
component in order to see the text
and date
for each post
.
- Open
./src/components/App.js
. - In the
render
method, update themap
to send the followingprops
intoPost
:text
- Should equal thepost
'stext
.date
- Should equal thepost
'sdate
.
- Open
./src/components/Post/Post.js
. - Update
POST DATE GOES HERE
to equal the value ofdate
onprops
. - Update
POST TEXT GOES HERE
to equal the value oftext
onprops
.
Detailed Instructions
Let's begin by opening ./src/components/App.js
. In the render
method, we'll need to update the map
to include two new props on the Post
component. The props
we'll need are: text
and date
. If we take a look at the API documentation we can see that a post
object has an id
, text
, and date
property. We can access the properties we need by using post.text
and post.date
.
{
posts.map( post => (
<Post key={ post.id }
text={ post.text}
date={ post.date } />
))
}
Now that our Post
component is receiving the text
and date
, we can render
them in the Post
component. Let's open ./src/components/Post/Post.js
. I'm going to destructure text
and date
off of props
at the top of the render
method for easier referencing.
const { text, date } = this.props;
We can then update POST DATE GOES HERE
and POST TEXT GOES HERE
to use these destructured values.
<span className="Post__date">- { date }</span>
<span className="Post__text">{ text }</span>
./src/components/App.js ( posts map )
{
posts.map( post => (
<Post key={ post.id }
text={ post.text}
date={ post.date } />
))
}
./src/components/Post/Post.js ( render method )
render() {
const { editing, showMasterMenu } = this.state;
const { text, date } = this.props;
return (
<section className="Post__parent" onClick={ this.hideMasterMenu }>
<div className="Post__master-controls">
<MasterControlIcon onClick={ this.toggleMasterMenu } />
<div className="Post__master-menu" style={ { display: showMasterMenu ? 'flex' : 'none' } }>
<span onClick={ this.showEdit }>Edit</span>
<span>Delete</span>
</div>
</div>
<div className="Post__meta-data">
<div className="Post__profile-picture">
<ProfileIcon />
</div>
<span className="Post__name">DevMountain</span>
<span className="Post__handle">@DevMountain</span>
<span className="Post__date">- { date }</span>
</div>
<div className="Post__content">
{
editing
?
<Edit text=""
hideEdit={ this.hideEdit } />
:
<span className="Post__text">{ text }</span>
}
</div>
<div className="Post__user-controls">
<ReplyIcon className="Post__control-icon" />
<FavoriteIcon className="Post__control-icon" />
<MessageIcon className="Post__control-icon" />
</div>
</section>
)
}
In this step, we'll create the updatePost
method to use axios
to update a post
's text
.
- Open
./src/components/App.js
. - In the
updatePost
method, useaxios
to send aPUT
request to the correct endpoint.- Remember that this method will need to work for any
post
. Hint:function parameters
. - Use the returned data from the request to update
posts
onstate
.
- Remember that this method will need to work for any
- Pass the
updatePost
method down as aprop
calledupdatePostFn
in theposts
map
. - Add a new
prop
to themap
calledid
that equals thepost
'sid
.
Detailed Instructions
Let's begin by opening ./src/components/App.js
. In the updatePost
method, we'll need to use axios
to make a PUT
request to the API. Using the API documentation, we can see that when editing a post the API is expecting a PUT
request at https://practiceapi.devmountain.com/api/posts
. We can also see that the endpoint uses the request query to determine the post
id
and uses the request body to determine the post
text
. Because the id
and text
of the post will be different every time the method is called we should use an id
and text
parameter for the method.
updatePost( id, text ) {
}
We can then use these parameters to construct our axios
request and use the returned data to update posts
on state
. When using axios.put()
the second argument is the request
body.
updatePost( id, text ) {
axios.put(`https://practiceapi.devmountain.com/api/posts?id=${ id }`, { text }).then( results => {
this.setState({ posts: results.data });
});
}
Now that our method is constructed, all we need to do is pass it down into the component
that needs it. In the render
method, let's update the map
to include a new prop
called updatePostFn
that equals the updatePost
method. We'll also add a new prop
called id
that equals the post
'd id
.
{
posts.map( post => (
<Post key={ post.id }
text={ post.text}
date={ post.date }
id={ post.id }
updatePostFn={ this.updatePost } />
))
}
./src/components/App.js
import React, { Component } from 'react';
import axios from 'axios';
import './App.css';
import Header from './Header/Header';
import Compose from './Compose/Compose';
import Post from './Post/Post';
class App extends Component {
constructor() {
super();
this.state = {
posts: []
};
this.updatePost = this.updatePost.bind( this );
this.deletePost = this.deletePost.bind( this );
this.createPost = this.createPost.bind( this );
}
componentDidMount() {
axios.get('https://practiceapi.devmountain.com/api/posts').then( results => {
this.setState({ posts: results.data });
});
}
updatePost( id, text ) {
axios.put(`https://practiceapi.devmountain.com/api/posts?id=${ id }`, { text }).then( results => {
this.setState({ posts: results.data });
});
}
deletePost() {
}
createPost() {
}
render() {
const { posts } = this.state;
return (
<div className="App__parent">
<Header />
<section className="App__content">
<Compose />
{
posts.map( post => (
<Post key={ post.id }
text={ post.text}
date={ post.date }
id={ post.id }
updatePostFn={ this.updatePost } />
))
}
</section>
</div>
);
}
}
export default App;
In this step, we'll make the Edit
button functional by using the updatePostFn
.
- Open
./src/components/Post/Post.js
. - Locate the
<div>
element with the class ofPost__content
.- In this
div
we either render theEdit
component or thepost
'stext
.
- In this
- Add a new
prop
to theEdit
component calledupdatePostFn
that equalsupdatePostFn
off ofprops
. - Add a new
prop
to theEdit
component calledid
that equalsid
off ofprops
. - Update the
text
prop
to equaltext
off ofprops
. - Open
./src/components/Post/Edit/Edit.js
. - Update the
updatePost
method to:- Call
updatePostFn
off ofprops
with the correctid
andtext
arguments. - Call
hideEdit
off ofprops
afterupdatePostFn
.hideEdit
is reponsible for toggling theEdit
component off after pressing theupdate
button.
- Call
Detailed Instructions
Let's begin by opening ./src/components/Post/Post.js
. In the render
method, there is a div
with the class of Post__content
that either renders a post
's text
or an Edit
component. The Edit
component is responsible for capturing a user's new text to assign to a post
. In order for the Edit
component to function, we'll need to pass the updatePostFn
prop
from Post.js
as a prop
into Edit.js
. We'll also need to pass down the post
's text
and id
so they can be used as the arguments for the updatePostFn
. You'll notice that a prop
called hideEdit
already exists, that method is responsible for hiding the Edit
component after updating a post
's text
.
<div className="Post__content">
{
editing
?
<Edit text={ text }
id={ id }
hideEdit={ this.hideEdit }
updatePostFn={ updatePostFn } />
:
<span className="Post__text">{ text }</span>
}
</div>
Now that we're passing down all the necessary props
into the Edit
component, let's open ./src/components/Post/Edit/Edit.js
and get the Edit
button functional. We'll need to update the updatePost
method. This method gets called when a user clicks on update
. The component keeps track of the new text value on its state
. Knowing this, we can call updatePostFn
with id
off of props
and text
off of state
. After we call updatePostFn
we should also call hideEdit
off of props
to hide the Edit
component.
updatePost() {
const { text } = this.state;
const { id, updatePostFn, hideEdit } = this.props;
updatePostFn( id, text );
hideEdit();
}
./src/components/Post/Post.js ( Post__content )
<div className="Post__content">
{
editing
?
<Edit text={ text }
id={ id } // Remember to destructure id off of props or use this.props.id
hideEdit={ this.hideEdit }
updatePostFn={ updatePostFn } // This should also be destructured from props or you'll need to reference this.props.updatePostFn />
:
<span className="Post__text">{ text }</span>
}
</div>
./src/components/Post/Edit/Edit.js ( updatePost method )
updatePost() {
const { text } = this.state;
const { id, updatePostFn, hideEdit } = this.props;
updatePostFn( id, text );
hideEdit();
}
In this step, we'll create the deletePost
method to use axios
to delete a post
and also make the Delete
button functional.
- Open
./src/components/App.js
. - In the
deletePost
method, useaxios
to send aDELETE
request to the correct endpoint.- Remember that this method will need to work for any
post
. Hint:function parameters
. - Use the returned data from the request to update
posts
onstate
.
- Remember that this method will need to work for any
- Pass the
deletePost
method down as aprop
calleddeletePostFn
in theposts
map
. - Open
./src/components/Post/Post.js
. - Update the
delete
span
element to use anonClick
event that callsdeletePostFn
off ofprops
.
Detailed Instructions
Let's begin by opening ./src/components/App.js
. We'll need to update the deletePost
method to make a DELETE
request using axios
. If we take a look at the API documentation we can see that the API expects a DELETE
request at https://practiceapi.devmountain.com/api/posts
. We can also see that the endpoint uses the request query to determine the id
of the post
to delete. Because the id
of the post
will be different every time we should use an id
parameter for this method.
deletePost( id ) {
}
We can then use the parameter to construct our axios
request and use the returned data to update posts
on state
.
deletePost( id ) {
axios.delete(`https://practiceapi.devmountain.com/api/posts?id=${ id }`).then( results => {
this.setState({ posts: results.data });
});
}
Now that our method is constructed, let's pass it down in the Post
component in our map
.
{
posts.map( post => (
<Post key={ post.id }
id={ post.id }
text={ post.text}
date={ post.date }
updatePostFn={ this.updatePost }
deletePostFn={ this.deletePost } />
))
}
We can then call this method and pass in the post
's id
using an onClick
event on the Delete
span
.
<span onClick={ () => deletePostFn( id ) }>Delete</span>
./src/components/App.js
import React, { Component } from 'react';
import axios from 'axios';
import './App.css';
import Header from './Header/Header';
import Compose from './Compose/Compose';
import Post from './Post/Post';
class App extends Component {
constructor() {
super();
this.state = {
posts: []
};
this.updatePost = this.updatePost.bind( this );
this.deletePost = this.deletePost.bind( this );
this.createPost = this.createPost.bind( this );
}
componentDidMount() {
axios.get('https://practiceapi.devmountain.com/api/posts').then( results => {
this.setState({ posts: results.data });
});
}
updatePost( id, text ) {
axios.put(`https://practiceapi.devmountain.com/api/posts?id=${ id }`, { text }).then( results => {
this.setState({ posts: results.data });
});
}
deletePost( id ) {
axios.delete(`https://practiceapi.devmountain.com/api/posts?id=${ id }`).then( results => {
this.setState({ posts: results.data });
});
}
createPost() {
}
render() {
const { posts } = this.state;
return (
<div className="App__parent">
<Header />
<section className="App__content">
<Compose />
{
posts.map( post => (
<Post key={ post.id }
id={ post.id }
text={ post.text}
date={ post.date }
updatePostFn={ this.updatePost }
deletePostFn={ this.deletePost } />
))
}
</section>
</div>
);
}
}
export default App;
./src/components/Post/Post.js ( Post__master-menu )
<div className="Post__master-menu" style={ { display: showMasterMenu ? 'flex' : 'none' } }>
<span onClick={ this.showEdit }>Edit</span>
<span onClick={ () => deletePostFn( id ) }>Delete</span> { /* Remember to destructure deletePostFn off of props or use this.props.deletePostFn */ }
</div>
In this step, we'll create the createPost
method to use axios
to create a new post
and also make the Compose
button functional.
- Open
./src/components/App.js
. - In the
createPost
method, useaxios
to send aPOST
request to the correct endpoint.- Remember that this method will need to work for any
post
. Hint:function parameters
. - Use the returned data from the request to update
posts
onstate
.
- Remember that this method will need to work for any
- Pass the
createPost
method down asprop
calledcreatePostFn
on theCompose
component. - Open
./src/components/Compose/Compose.js
. - Update the
createPost
method to:- Call the
createPostFn
off ofprops
with the correcttext
argument. - Use
setState
to reset the value oftext
onstate
to an empty string.
- Call the
Detailed Instructions
Let's begin by opening ./src/components/App.js
. We'll need to update the createPost
method to make a POST
request using axios
. If we take a look at the API documentation we can see that the API expects a POST
request at https://practiceapi.devmountain.com/api/posts
. We can also see that the endpoint uses the request body to determine the text
of the post
to create. Because the text
of the post
will be different every time, we should use a text
parameter for this method.
createPost( text ) {
}
We can then use the parameter to construct our axios
request and use the returned data to update posts
on state
.
createPost( text ) {
axios.post('https://practiceapi.devmountain.com/api/posts', { text }).then( results => {
this.setState({ posts: results.data });
});
}
Now that our method is constructed, let's pass it down in the Compose
component.
<Compose createPostFn={ this.createPost } />
We can then update the createPost
method in ./src/components/Compose/Compose.js
to call createPostFn
off of props
with the value of text
on state
. After calling createPostFn
we should also reset the value of text
on state
using setState
.
createPost() {
const { text } = this.state;
const { createPostFn } = this.props;
createPostFn( text );
this.setState({ text: '' });
}
./src/components/App.js
import React, { Component } from 'react';
import axios from 'axios';
import './App.css';
import Header from './Header/Header';
import Compose from './Compose/Compose';
import Post from './Post/Post';
class App extends Component {
constructor() {
super();
this.state = {
posts: []
};
this.updatePost = this.updatePost.bind( this );
this.deletePost = this.deletePost.bind( this );
this.createPost = this.createPost.bind( this );
}
componentDidMount() {
axios.get('https://practiceapi.devmountain.com/api/posts').then( results => {
this.setState({ posts: results.data });
});
}
updatePost( id, text ) {
axios.put(`https://practiceapi.devmountain.com/api/posts?id=${ id }`, { text }).then( results => {
this.setState({ posts: results.data });
});
}
deletePost( id ) {
axios.delete(`https://practiceapi.devmountain.com/api/posts?id=${ id }`).then( results => {
this.setState({ posts: results.data });
});
}
createPost( text ) {
axios.post('https://practiceapi.devmountain.com/api/posts', { text }).then( results => {
this.setState({ posts: results.data });
});
}
render() {
const { posts } = this.state;
return (
<div className="App__parent">
<Header />
<section className="App__content">
<Compose createPostFn={ this.createPost } />
{
posts.map( post => (
<Post key={ post.id }
id={ post.id }
text={ post.text}
date={ post.date }
updatePostFn={ this.updatePost }
deletePostFn={ this.deletePost } />
))
}
</section>
</div>
);
}
}
export default App;
./src/components/Compose/Compose.js ( createPost method )
createPost() {
const { text } = this.state;
const { createPostFn } = this.props;
createPostFn( text );
this.setState({ text: '' });
}
Update the project to allow the Search
bar to find posts
that contain a certain string. Example: If a user searchs for blue, the Feed of posts should only display posts that contain the word blue. To filter by multiple words be sure to use the encodeURI
JavaScript function.
Axios
Props
If you see a problem or a typo, please fork, make the necessary changes, and create a pull request so we can review your changes and merge them into the master repo and branch.
© DevMountain LLC, 2017. Unauthorized use and/or duplication of this material without express and written permission from DevMountain, LLC is strictly prohibited. Excerpts and links may be used, provided that full and clear credit is given to DevMountain with appropriate and specific direction to the original content.