axios with react

Build a Simple Blog using Axios With React


In this article, we are going to learn how to use Axios with React to make API requests and also how to handle the response. We’d learn this by building a simple blog using a fake API server.

What is Axios?

Axios is a javascript library built for making HTTP requests from NodeJS or the browser. These include POST, GET, and many other forms of requests. It can be be used in vanilla javascript as well as many other frameworks but our use-case today is React.

Communicating with APIs before was usually done with the fetch API. Axios doesn’t replace this but only makes the communication easier. It also has better error handling, easy header configurations and the readability of code is improved.

The features of Axios according to the NPM documentation are:

  • Make XMLHttpRequests from the browser
  • Make HTTP requests from node.js
  • Automatic transforms for JSON data
  • Transform request and response data

You can get more features in the npm documentation.

Blog Project

We’d be using a popular public API – http://jsonplaceholder.typicode.com/. We can get a list of posts by hitting the /posts endpoint, edit and delete posts by /post/id and so on. You can check the site to see how to use their endpoints.

Create a project folder and change directory

To use Axios in React, you’d need to install the package. Before you do that, we need our project folder. Quickly set up a project using create-react-app.

  • Install the package – npm install -g create-react-app
  • Create the project – create-react-app axios-with-react
  • Change directory – cd axios-with-react.
  • Start server – npm run start
  • Go to the browser and enter localhost:3000, you’d see the project which the package sets for us.

Install Axios Library

npm install axios --save

Making Requests with Axios

Axios handles request methods such as POST, GET, PUT, DELETE, PATCH, etc. We’d be seeing some of the actions in our project.

GET Request

Change the contents of App.js to the following

import React from 'react';
import './App.css';
import axios from 'axios';

class App extends React.Component {
  state = {
    id:  '',
    title: '',
    body: '',
    data: []
  }
  
  componentDidMount() {
    axios.get('https://jsonplaceholder.typicode.com/posts')
    .then(res => {
      let newData = res.data.slice(0,5);
      this.setState({
        id: newData[newData.length - 1].id + 1,
        data: newData
      }, () => console.log(this.state.id))
      console.log(newData)
    })
    .catch(err => console.log("Couldn't fetch data. Error: " + err))
  }

  render() {

    return (
      <div className='ArticleContainer'>
        <h1>Simple blog with React</h1>
        <div className='AddArticle'>
          <b>id of article: </b>
          <input type='number' value={this.state.id} />
          <form>
          <input type='text' placeholder='Title' value={this.state.title} />
          <textarea placeholder='Enter Body' value={this.state.body}>
          </textarea>
          <input type='submit' value='Add/Update Post'/>
          </form>
        </div>
        {
          this.state.data.length === 0 ?
            <p>Loading Posts...</p>
          :
            this.state.data.map((post, index) => (
              <article key={index}>
                <h2>{index + 1}. {post.title}</h2>
                <p>{post.body.substr(0, 100)}...</p>
                <button className='delete'>Delete</button>
                <button className='edit'>Edit</button>
              </article>
            ))
        }
      </div>
    )
  }
  
}

export default App;

componentDidMount is the safest stage in React components for making API requests. It is a cycle in a component’s life which ensures that the elements have been mounted to the DOM. Imagine making a request which would take a couple of time. It may keep our DOM empty until the request is completed.

First, we have Loading posts showing on the screen until the data is fetched.

Check the console of your browser, you either get an error or the response from the endpoint we hit. From the response, we understand the structure and know-how to place the data on our frontend.

Illustrating the response gotten

Notice we have only 5 results, that’s because we sliced the array. We are supposed to get over 100 results. Let’s add our CSS to see our beautiful frontend. Copy the following to App.css

* {
  box-sizing: border-box;
}
.ArticleContainer {
  width: 500px;
  margin: 0 auto;
  margin-top: 20px;
}
.AddArticle {
  display: flex;
  flex-direction: column;
  width: 100%;
}
.AddArticle input, .AddArticle textarea {
  width: 100%;
  border-radius: 5px;
  border: 1px solid #ddd;
  margin: 5px 0;
  padding: 5px;
}
.AddArticle input[type='submit'] {
  padding: 5px;
  background-color: greenyellow;
  border: none;
  cursor: pointer;
}
h1 { text-align: center; }
article {
  padding: 10px;
  border: 1px solid #ddd;
  margin: 10px auto 20px;
  width: 100%;
}
article h2 {
  color: orange;
  font-size: 30px;
  margin: 5px 0;
}
article button {
  width: 50px;
  margin: 0 5px;
  border: none;
  padding: 5px;
  cursor: pointer;
}
article .delete {color: red;}
article .edit {color:green;}
article .cancel {color: orange;}

We should have this;

We have five posts each with their id. The new article will have an id of 6. We can add more posts by making a post request to the server.

POST Request

We have the endpoint https://jsonplaceholder.typicode.com/posts which allows us to create posts. Note that, as said on their website, the data would not be created on their server. It will only be faked as you’ll get a positive response for illustration purposes.

We have a button which handles creation and edition of posts – Add/Update Post button. I configured it this way so that we do not have to route to any other page to see all requests in action.

A method will be called by the button which will either add or update a post. In your code above, add the following methods immediately after the state declaration

...
changeId = e => {
    let id = e.target.value;
    this.setState({
      id: id
    })
  }
  
  changeTitle = e => {
    let title = e.target.value;
    this.setState({
      title: title
    })
  }

  changeBody = e => {
    let body = e.target.value;
    this.setState({
      body: body
    })
  }

  addOrUpdatePost = e => {
    e.preventDefault();
    if(this.state.title === '' || this.state.body === '' || this.state.id === '') {
      alert('No field should be empty');
      return;
    } else if(this.state.id > this.state.data.length + 1) { 
      alert('Please use the next id');
    } else {
      if(this.state.data[this.state.id - 1] !== undefined) {
        // update the post
      } else {
        // new post
      axios.post("https://jsonplaceholder.typicode.com/posts", {
        id: this.state.id + 1,
        title: this.state.title,
        body: this.state.body
      })
      .then(res => {
        console.log(res);
        let newPost = res.data;
        let newData = [...this.state.data, newPost];
        this.setState({
          id: this.state.id + 1,
          title: '',
          body: '',
          data: newData
        });
      })
      .catch(err => console.log(err));
      }
    }
...
  • First, we have three methods that update the id, title, and body of the state respectively when called upon.
  • Next, we check to see if the states are empty which would result in an alert box, stating that “No field should be empty”
  • We also confirm that the current id has either been used or just greater than the highest id with 1. If it isn’t, we have an alert box which says “Please use the next id”
  • A decision has to be made if a new post is to be created or a previous post is to be updated. We check if the id exists by this.state.data[this.state.id - 1] !== undefined . If it returns undefined, it means the id doesn’t exist so we can create a new post there. We’d look at the update process soon.
  • Let’s add these methods to our elements. Update your code to this
...
  render() {

    return (
      <div className='ArticleContainer'>
        <h1>Simple blog with React</h1>
        <div className='AddArticle'>
          <b>id of article: </b>
          <input type='number' onChange={this.changeId} value={this.state.id} />
          <form>
          <input onChange={this.changeTitle} type='text' placeholder='Title' value={this.state.title} />
          <textarea onChange={this.changeBody} placeholder='Enter Body' value={this.state.body}>
          </textarea>
          <input onClick={this.addOrUpdatePost} type='submit' value='Add/Update Post'/>
          </form>
        </div>
        {
          this.state.data.length === 0 ?
            <p>Loading Posts...</p>
          :
            this.state.data.map((post, index) => (
              <article key={index}>
                <h2>{index + 1}. {post.title}</h2>
                <p>{post.body.substr(0, 100)}...</p>
                <button onClick={() => this.deletePost(index)} className='delete'>Delete</button>
                <button onClick={() => this.editPost(index, post.title, post.body)} className='edit'>Edit</button>
              </article>
            ))
        }
      </div>
    )
  }
...
  • When the add button is clicked, we use the post method to send our new post through the API. Remember that it’s a fake live server, so it only returns a reponse which contains our new post as an object with a statusText of created. We add new data to the previous data and update the state. When the component is re-rendered, our post will be the sixth post on the list.
  • Here is our response to the console.
Illustrating the response gotten

Notice the editPost and deletePost method in our code above. It allows us to edit or delete an individual posts. We’d come to that shortly.

Update Request

Our API gives us endpoints which we could use to update resources. For example, we could update the post with id 1, by hitting this endpoint – https://jsonplaceholder.typicode.com/posts/1

Let’s add the editPost method. Add the following immediately after the changeBody method

...  
  editPost = (postIndex, title, body) => {
    this.setState({
      id: postIndex + 1,
      title: title,
      body: body
    })

  }
...

The function takes the postIndex, title and body argument and resets the state. Remember that in setting the state, the input fields reflect the values. Also, remember that our button handles add and update. Since the id has been used before, this.state.data[this.state.id - 1] would not return undefined. Add the following after // update the post in the addOrUpdatePost method

...        
   axios.put(`https://jsonplaceholder.typicode.com/posts/${this.state.id}`, {
          id: this.state.id ,
          title: this.state.title,
          body: this.state.body
        }).then(res => {
          let updatedData = [...this.state.data];
          updatedData[this.state.id - 1] = res.data;
          this.setState({
            id: updatedData.length + 1,
            title: '',
            body: '',
            data: updatedData
          })
          console.log(res)
        })
        .catch(err => console.log(err));
...

If successful, you’ll get this.

Illustrating the response gotten

As expected, it doesn’t update on their server. It only returns our new object to use which we update in our data state thereby re-rendering the DOM. You’d notice we are using a put method on the API – https://jsonplaceholder.typicode.com/posts/1. I edited the post with the id of 1 by changing the title to “Edited”

Delete Request

We can also hit endpoints of posts and delete a post by their id. We already have the delete button so let’s add the method which it calls on clicking. Add the following before componentDidMount

  deletePost = postIndex => {
    axios.delete(`https://jsonplaceholder.typicode.com/posts/${postIndex}`)
    .then(res => {
      let newData = [...this.state.data];
      newData.splice(postIndex, 1);
      this.setState({
        id: newData.length + 1,
        title: '',
        body: '',
        data: newData
      })
      console.log(res)
    })
    .catch(err => console.log(err));
  }

The post is deleted when the delete method is called. The method takes the index, searches for it in our data and removes it thereby updating the state.

Illustrating the response gotten from API

If you notice, the object is empty, it has been deleted. And as expected, it wasn’t deleted on the server.

The whole code for the project (except the CSS);

import React from 'react';
import './App.css';
import axios from 'axios';

class App extends React.Component {

  state = {
    id: '',
    title: '',
    body: '',
    data: []
  }

  changeId = e => {
    let id = e.target.value;
    this.setState({
      id: id
    })
  }
  
  changeTitle = e => {
    let title = e.target.value;
    this.setState({
      title: title
    })
  }

  changeBody = e => {
    let body = e.target.value;
    this.setState({
      body: body
    })
  }

  editPost = (postIndex, title, body) => {
    this.setState({
      id: postIndex + 1,
      title: title,
      body: body
    })

  }

  addOrUpdatePost = e => {
    e.preventDefault();
    if(this.state.title === '' || this.state.body === '' || this.state.id === '') {
      alert('No field should be empty');
      return;
    } else if(this.state.id > this.state.data.length + 1) { 
      alert('Please use the next id');
    } else {
      if(this.state.data[this.state.id - 1] !== undefined) {
        axios.put(`https://jsonplaceholder.typicode.com/posts/${this.state.id}`, {
          id: this.state.id ,
          title: this.state.title,
          body: this.state.body
        }).then(res => {
          let updatedData = [...this.state.data];
          updatedData[this.state.id - 1] = res.data;
          this.setState({
            id: updatedData.length + 1,
            title: '',
            body: '',
            data: updatedData
          })
          console.log(res)
        })
        .catch(err => console.log(err));
      } else {
      axios.post("https://jsonplaceholder.typicode.com/posts", {
        id: this.state.id + 1,
        title: this.state.title,
        body: this.state.body
      })
      .then(res => {
        console.log(res);
        let newPost = res.data;
        let newData = [...this.state.data, newPost];
        this.setState({
          id: this.state.id + 1,
          title: '',
          body: '',
          data: newData
        });
      })
      .catch(err => console.log(err));
      }
    }
  }

  deletePost = postIndex => {
    axios.delete(`https://jsonplaceholder.typicode.com/posts/${postIndex}`)
    .then(res => {
      let newData = [...this.state.data];
      newData.splice(postIndex, 1);
      this.setState({
        id: newData.length + 1,
        title: '',
        body: '',
        data: newData
      })
      console.log(res)
    })
    .catch(err => console.log(err));
  }
  
  componentDidMount() {
    axios.get('https://jsonplaceholder.typicode.com/posts')
    .then(res => {
      let newData = res.data.slice(0,5);
      this.setState({
        id: newData[newData.length - 1].id + 1,
        data: newData
      }, () => console.log(this.state.id))
      console.log(newData)
    })
    .catch(err => console.log("Couldn't fetch data. Error: " + err))
  }

  render() {

    return (
      <div className='ArticleContainer'>
        <h1>Simple blog with React</h1>
        <div className='AddArticle'>
          <b>id of article: </b>
          <input type='number' onChange={this.changeId} value={this.state.id} />
          <form>
          <input onChange={this.changeTitle} type='text' placeholder='Title' value={this.state.title} />
          <textarea onChange={this.changeBody} placeholder='Enter Body' value={this.state.body}>
          </textarea>
          <input onClick={this.addOrUpdatePost} type='submit' value='Add/Update Post'/>
          </form>
        </div>
        {
          this.state.data.length === 0 ?
            <p>Loading Posts...</p>
          :
            this.state.data.map((post, index) => (
              <article key={index}>
                <h2>{index + 1}. {post.title}</h2>
                <p>{post.body.substr(0, 100)}...</p>
                <button onClick={() => this.deletePost(index)} className='delete'>Delete</button>
                <button onClick={() => this.editPost(index, post.title, post.body)} className='edit'>Edit</button>
              </article>
            ))
        }
      </div>
    )
  }
  
}

export default App;

We were working on a demo server, that’s why I had to manually update all properties of the state every time we make a request. If we were using a server which gave us access to manipulate the data, all we’d have to do is make our requests and update the state to the current data. We wouldn’t have to filter by the index of the post.

Check out the npm documentation for more information about Axios.

I hope with this article, you have seen how easy it is making requests with Axios.

You can access CodeSource here.


Share on social media

//