Consuming a REST API in React with Axios
Tags: javascript, webdev, react, django
Published: 2021-11-16 08:33:38

Welcome to the last part of this series. Here we'll be creating the frontend for the Notes application. Familiarity with react is needed for this tutorial but you don't need to be an expert, basic knowledge is good enough for you to follow and understand. The first objective is to get the app up and running, styling will be done at the end.

If you come across this part first, you can check out parts 1 and 2. We already handled the backend setup and development in those tutorials.

We'll continue from where we stopped in part 2; so this would be easy to follow as well.

Let's get started!


##Set-up react application directory
Navigate to the frontend application directory.
```javascript
cd frontend
``` 
There happens to be a lot of files in the frontend directory that we won't make use of in the react application. 
###public folder
The important file here is the `index.html` file. You can delete all other files here. Don't forget to go inside the index.html file to delete the links to the `manifest.json and logos`. You can keep the react favicon or change it to a favicon of your choice. You can customize yours [here](https://favicon.io/).
###src folder
Delete all the files in the `src` folder except the `index.js` file. Then create two new folders `components` and `css` in the `src` folder. Inside the components folder, create the following files. `App.jsx` `Notes.jsx` and `List.jsx` and inside the css folder create the `index.css` file.
The frontend directory should currently look like 👇
![Frontend directory](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ho1d397jiycr6sqpnuxy.png)
###index.js
Remove the `webvitals` import and the webvitals function at the end of the file as we won't be making use of them. Since we have changed the location of the App.jsx component we need to change the `path` of the App import to this
```javascript
import App from './components/App'
``` 
and that of the css import to
```javascript
import './css/index.css'
```
The `index.js` file should look like 👇
```javascript
import React from 'react'
import ReactDOM from 'react-dom'
import './css/index.css'
import App from './components/App'

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
```
To make requests to the API endpoints on the Django backend server, we will need a JavaScript library called `axios`. 

Axios is an HTTP client library that allows you to make requests to a given API endpoint, you can find out more [here](https://axios-http.com/docs/intro).

First, we'll install it using npm:
```javascript
npm install axios
```
###package.json
Next, open the package.json file and add the proxy below the `"private": true,` line so it ends up like 👇.
```javascript
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "proxy": "http://localhost:8000",
```
This will make it possible for you to use `relative paths` when you are making the API requests. Instead of making use of `http://localhost:8000/notes/` you can simply make use of `/notes/`. Seems like a great idea right?. You'll see it in action shortly. Now let's work on the component files.
###List.jsx
Let's start with the List component. We won't be doing much here yet, we just need to simply declare and export the function.
```javascript
function List(){
  
    return (
        <div className="note">

        </div>
    )
  }

export default List
```
###Notes.jsx
First we import the required hooks; `useState` and `useEffect`. You can read more about react hooks [here](https://reactjs.org/docs/hooks-intro.html). We also need to import `axios` and the List component we created above. 
```
import {useState, useEffect} from "react"
import axios from "axios"
import  List from "./List"
```

####useState
Next, we create the Note function in which we will make use of the useState hook. In the first line, we declare the `state variable` as notes with an initial state of `null`. 

The second line is to handle the state of the form data. Here we declare the `state variable` as formNote with empty strings as its initial state.
```javascript
function Note() {
    const [notes , setNewNotes] = useState(null)
    const [formNote, setFormNote] = useState({
          title: "",
          content: ""
          })
    }
```
####Please note that every other function created below should be inside the `Note` function above.
####useEffect
We'll also use the useEffect hook, so that the `getNotes` function executes right after the render has been displayed on the screen.
```javascript
    useEffect(() => {
      getNotes()
        } ,[])
```
To prevent the function from running in an infinite loop, you can pass an empty array ([]) as a second argument. This tells React that the effect doesn’t depend on any values from props or state, so it never needs to be re-run.

####GET API function
```javascript
function getNotes() {
  axios({
      method: "GET",
      url:"/notes/",
    }).then((response)=>{
      const data = response.data
      setNewNotes(data)
    }).catch((error) => {
      if (error.response) {
        console.log(error.response);
        console.log(error.response.status);
        console.log(error.response.headers);
        }
    })}
```
Here we are declaring the request method type as `GET` and then passing the relative path `/notes/` as the URL. If we had not added the proxy `"http://localhost:8000"` to the package.json file. We would need to declare the URL here as `"http://localhost:8000/notes/"`. I believe the method we used makes the code cleaner.

When the `GET` request is made with axios, the data in the received response is assigned to the `setNewNotes` function, and this updates the state variable `notes` with a new state. Thus the value of the state variable changes from `null` to the `data in the received response`.

We also have the error handling function in case something goes wrong with the get request.
####POST API function
```javascript
function createNote(event) {
    axios({
      method: "POST",
      url:"/notes/",
      data:{
        title: formNote.title,
        content: formNote.content
       }
    })
    .then((response) => {
      getNotes()
    })

    setFormNote(({
      title: "",
      content: ""}))

    event.preventDefault()
}
```
Here we are declaring the request method type as `POST` and then passing the relative path `/notes/` as the URL. We also have an additional field here `data`. This will contain the data which we'll send to the backend for processing and storage in the database. That is the data from the title and content inputs in the form.

When the `POST` request is made with Axios, we don't process the response (remember that this was mentioned in part 2 when we were setting up the POST API function); we just use the response function to recall the `getNotes` function so that the previous notes can be displayed together with the newly added note.

After this, we reset the form inputs to empty strings using the `setFormNote` function. Then we also have to ensure that the form submission does not make the page reload so we add the `event.preventDefault` function which prevents the default action of the form submission.
####DELETE API function
```javascript
function DeleteNote(id) {
    axios({
      method: "DELETE",
      url:`/notes/${id}/`,
    })
    .then((response) => {
      getNotes()
    });
}
```
We create the function with an `id` parameter so that we can pass the id of the particular note which we want to delete as an argument later on.

When the `DELETE` request is made with Axios, we don't process the response as well; we just use the response function to call the `getNotes` function so that the notes get method can get executed once again and we'll now see the remaining notes retrieved from the database.

#### form input change 
We need to ensure that the input is a controlled one, so we handle the changes with the code below. 
```javascript
function handleChange(event) { 
  const {value, name} = event.target
  setFormNote(prevNote => ({
      ...prevNote, [name]: value})
  )}
```
The function monitors every single change in the form inputs and updates/delete where necessary. Without this function, you won't see what you are typing in the form input fields and the values of your input elements won't change as well. We de-structure event.target to get the value and name then we use the spread syntax to retain the value of the previous input and finally we assign a new value to the particular input being worked on.

####return
Now we return the React elements to be displayed as the output of the `Note` function.
```javascript

  return (
<div className=''>
          
      <form className="create-note">
          <input onChange={handleChange} text={formNote.title} name="title" placeholder="Title" value={formNote.title} />
          <textarea onChange={handleChange} name="content" placeholder="Take a note..." value={formNote.content} />
          <button onClick={createNote}>Create Post</button>
      </form>
          { notes && notes.map(note => <List
          key={note.id}
          id={note.id}
          title={note.title}
          content={note.content} 
          deletion ={DeleteNote}
          />
          )}

    </div>
  );
```
In the form, we add the input and text area elements. Then we add the onChange event handler which calls the handleChange function when we make any change to the input fields. Then in the next line where we render the `List` component, we need to first confirm that at least one single note was retrieved from the database so that we don't pass null data to the `List` component.

If notes were actually retrieved with the GET function; we pass the content of the data (id, title, content) and also the delete function to the `List` component.

Finally don't forget to export the `Note` component so it can be used in the `App.jsx` file.
```javascript
export default Note;
```
The Notes.jsx file should currently look like 👇
```javascript
import {useState, useEffect} from "react";
import axios from "axios";
import List from "./List"

function Note() {

    const [notes , setNewNotes] = useState(null)
    const [formNote, setFormNote] = useState({
      title: "",
      content: ""
    })

    useEffect(() => {
      getNotes()
        } ,[])

    function getNotes() {
      axios({
          method: "GET",
          url:"/notes/",
        }).then((response)=>{
          const data = response.data
          setNewNotes(data)
        }).catch((error) => {
          if (error.response) {
            console.log(error.response);
            console.log(error.response.status);
            console.log(error.response.headers);
            }
        })}

    function createNote(event) {
        axios({
          method: "POST",
          url:"/notes/",
          data:{
            title: formNote.title,
            content: formNote.content
           }
        })
        .then((response) => {
          getNotes()
        })
    
        setFormNote(({
          title: "",
          content: ""}))
    
        event.preventDefault()
    }
            
    function DeleteNote(id) {
        axios({
          method: "DELETE",
          url:`/notes/${id}/`,
        })
        .then((response) => {
          getNotes()
        })
    }

    function handleChange(event) { 
        const {value, name} = event.target
        setFormNote(prevNote => ({
            ...prevNote, [name]: value})
        )}


  return (

     <div className=''>
          
        <form className="create-note">
          <input onChange={handleChange} text={formNote.title} name="title" placeholder="Title" value={formNote.title} />
          <textarea onChange={handleChange} name="content" placeholder="Take a note..." value={formNote.content} />
          <button onClick={createNote}>Create Post</button>
        </form>

        { notes && notes.map(note => <List
        key={note.id}
        id={note.id}
        title={note.title}
        content={note.content} 
        deletion ={DeleteNote}
        />
        )}

    </div>

  );
}

export default Note;

```  
###List.jsx
Now we have to go back to the `List.jsx` file to finish creating the `List` component.
```javascript
function List(props){
      function handleClick(){
    props.deletion(props.id)
  }
    return (
        <div className="note">
          <h1 >  Title: {props.title} </h1>
          <p > Content: {props.content}</p>
          <button onClick={handleClick}>Delete</button>
        </div>
    )
  }

export default List;
```
Here we access the data sent from the Note function using `props`; which gives us access to the title, content and id of the note. We pass the id to an onClick function which in turn calls the delete function in the Note function with `id` as the argument.

Note: If you pass the delete function into the onClick function directly, the delete function will run automatically and delete all your notes. A solution to this is to pass the delete function into a function called by the onClick function just like we did above.

###App.jsx
Now let us import the `Note` function into the `App.jsx` file.
```javascript
import Note from "./Notes"

function App() {
    
  return (
    <div className='App'>
      <Note />

    </div>
  );
}

export default App;
```
To test the current state of the application, run:
```javascript
    npm run build
```
then return to the project1 directory that contains the `manage.py` file
```javascript
   cd ..
```
Finally we run:
```javascript
   python manage.py runserver
```
Here is what the fully functional application looks like now 👇.
![Test Application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sj9we2ho3bi6eqktppwp.png)

##Styling
The final part of this tutorial is to style the `Notes` application and make it end up looking like👇.
![Styled Application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ow312bkbpq96ctotqy2d.png)

Return to the frontend directory 
```javascript
   cd frontend
```

####Material UI icon
You need to install material ui icon to get the `+` icon. Run:
```javascript
npm install @material-ui/icons
``` 
####Notes.jsx
Import `AddIcon` from the installed material ui icon package into the `Notes` component
```javascript
import AddIcon from "@material-ui/icons/Add";
```
Next, we want to make the text input and add button hidden until the text area input is clicked, we'll use `useState` hooks once again to achieve this.
```javascript
const [isExpanded, setExpanded]= useState(false)
const [rows, setRows]= useState(1)
```
The first line displays or hides the text input and add button based on the state(false or true). Here we declare the `state variable` as isExpanded with an initial state of `false` so the text input and add button are hidden when the page is loaded.
![Initial state](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i4th1lfxietkkgive43y.png)
The second line determines the height of the text area input. Here we declare the `state variable` as rows with an initial state of `1`

```javascript
function NoteShow(){
    setExpanded(true)
    setRows(3)
   }
```
Next, we create a new function `Noteshow` which gets called when the text area input is clicked. 

Let's make the necessary changes to the form inputs as well;
```javascript
<form className="create-note">
  {isExpanded && <input onChange={handleChange} text={formNote.title} name="title" placeholder="Title" value={formNote.title} />}
  <textarea onClick={NoteShow} onChange={handleChange} name="content" placeholder="Take a note..." rows={rows} value={formNote.content} />
  {isExpanded && <button onClick={createNote}>
                    <AddIcon />
                </button>}
</form>
```
The `isExpanded` condition is added to the text input and button as explained earlier. When the textarea input is clicked, the `NoteShow` function is called and two things happen.
i) the `setExpanded` function is called with the argument `true` which changes the state to true and then the hidden components are displayed 
ii) the `setRows` function is called with the argument `3`
which changes the rows attribute of the textarea input to 3 thus increasing the height of the textarea input.

![Expanded state](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qn3zmmsic7hmf3llf42f.png)

Then we add the imported icon to the button.

Finally, we add `setExpanded(false)` to the end of the createNote function 
```javascript
function createNote(event) {
        axios({
          method: "POST",
          url:"/notes/",
          data:{
            title: formNote.title,
            content: formNote.content
           }
        })
        .then((response) => {
          getNotes()
        })

        setFormNote(({
          title: "",
          content: ""}))
        setExpanded(false)
        event.preventDefault()
    }
```
so that upon submission of the form, the text input and button both go back to their hidden state. 

This is the final state of the Note.jsx component 👇.
```javascript
import {useState, useEffect} from "react";
import axios from "axios";
import List from "./List"
import AddIcon from "@material-ui/icons/Add";

function Note() {
    const [isExpanded, setExpanded]= useState(false)
    const [rows, setRows]= useState(1)

    const [notes , setNewNotes] = useState(null)
    const [formNote, setFormNote] = useState({
      title: "",
      content: ""
    })

    useEffect(() => {
      getNotes()
        } ,[])

    function getNotes() {
      axios({
          method: "GET",
          url:"/notes/",
        }).then((response)=>{
          const data = response.data
          setNewNotes(data)
        }).catch((error) => {
          if (error.response) {
            console.log(error.response);
            console.log(error.response.status);
            console.log(error.response.headers);
            }
        })}

    function createNote(event) {
        axios({
          method: "POST",
          url:"/notes/",
          data:{
            title: formNote.title,
            content: formNote.content
           }
        })
        .then((response) => {
          getNotes()
        })

        setFormNote(({
          title: "",
          content: ""}))
        setExpanded(false)
        event.preventDefault()
    }

    function DeleteNote(id) {
        axios({
          method: "DELETE",
          url:`/notes/${id}/`,
        })
        .then((response) => {
          getNotes()
        })
    }

    function handleChange(event) { 
        const {value, name} = event.target
        setFormNote(prevNote => ({
            ...prevNote, [name]: value})
        )}

    function NoteShow(){
        setExpanded(true)
        setRows(3)
      }

  return (

     <div className=''>

        <form className="create-note">
          {isExpanded && <input onChange={handleChange} text={formNote.title} name="title" placeholder="Title" value={formNote.title} />}
          <textarea onClick={NoteShow} onChange={handleChange} name="content" placeholder="Take a note..." rows={rows} value={formNote.content} />
          {isExpanded && <button onClick={createNote}>
                            <AddIcon />
                        </button>}
        </form>

        { notes && notes.map(note => <List
        key={note.id}
        id={note.id}
        title={note.title}
        content={note.content} 
        deletion ={DeleteNote}
        />
        )}

    </div>

  );
}

export default Note;

```
####Header.jsx
Create a new component `Header.jsx` in the components folder. This will hold our header elements.
```javascript
function Header() {
  return (
    <header>
      <h1>Notes</h1>
    </header>
  );
}
export default Header;
```
####Footer.jsx 
Create a new component `Footer.jsx` in the components folder.This will hold our footer elements.
```javascript
function Footer() {
  const year = new Date().getFullYear();
  return (
    <footer>
      <p>Copyright ⓒ {year}</p>
    </footer>
  );
}
export default Footer;
``` 
Here we simply run the `Date().getFullYear()` method to get the year of the current date and pass it to the `p` element in our footer.

####App.jsx
We need to import the Header and Footer components into the `App.jsx` file and then call them. 
```javascript
import Note from "./Notes"
import Header from "./Header"
import Footer from "./Footer"

function App() {
    
  return (
    <div className='App'>

      <Header />
      <Note />
      <Footer />

    </div>
  );
}
export default App;
```
####CSS
Head over to the [github repo](https://github.com/Faruqt/React-Django/blob/master/project1/frontend/src/css/index.css) for the css codes; the `classNames` have already been included while we were building the application. 

We have completed the development of the Notes Application with `CREATE`,`READ` and `DELETE` functionalities. You can explore and have fun with your application now. 

To test it run:
```javascript
    npm run build
```
then return to the project1 directory that contains the `manage.py` file
```javascript
   cd ..
```
Finally we run:
```javascript
   python manage.py runserver
```
You should see the new magic we just created.

Here is the link to the [github repo] (https://github.com/Faruqt/React-Django) for this project. Cheers!!!

If you have any questions, feel free to drop them as a comment or send me a message on [Linkedin](https://www.linkedin.com/in/faruq-abdulsalam) or [Twitter](https://twitter.com/Ace_II) and I'll ensure I respond as quickly as I can. Ciao 👋
![obito](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gpnkg8k2qol3r3gm83rf.gif)