React Starter Project | Consuming and Displaying API Data

React Starter Project | Consuming and Displaying API Data

scotty profile picture

Scotty Parlor

July 25, 2020

Read Time 7 min

I recently published a new vid on a job post by Cybercoders that requires REST API experience as well as React. I covered the Django Rest Framework tutorial HERE and wanted to follow up with a separate minimalist React tutorial.

In case you are not interested in the Rest tutorial I did, here is gist. It can quickly be pulled from my docker repo scottyfullstack/basic-rest-api.  This api is meant for practicing purposes only. There is no authentication required to GET, POST, or DELETE and saves data to a sqlite file in the container itself.

What we want to do is show a basic knowledge of React consuming that API and displaying the response json data in a way that the user understands (i.e. UX/UI).

You can fire up the api with

sudo docker run -it -d -p 8000:8000 scottyfullstack/basic-rest-api

Requirements

Node and npm

We are going to create an app that allows you to enter in product details (title, description, and price) and POST to the django rest api above that will save our data to a sqlite file. It will also GET via the api and we will map that response data to our react app.

Head to the directory you want your project in and run the react boilerplate cli command:

npx create-react-app uix

uix is what i'm naming my app...you can name it whatever you want.

Once it complete cd in and install the following packages (notice there is a package.json file in the top level):

cd uix/
npm install --save node-sass sass-loader
npm install --save axios

The node-sass and sass-loader will allow us to compile scss/sass files into css. This is not required, but if you've ever built a frontend app without bootstrap you will understand the significant advantage to getting used to scss/sass.

Axios is a library that allows us to easily interact with our API.

Now cd into src/ and make the following directories:

cd src/
mkdir components containers

For this tutorial, we will use the components and containers organization. Our container will be a container app that has state. The components could be reusable with props. This setup is not required by React, but I have found this is best practice and keeps projects organized enough for people to pick up work after you (and..its kind of the point of react).

While here in src, let's open the App.js file and remove mostly everything.

Here is what my file looks like:

uix/src/App.js

import React from 'react';
import './App.css';
import Homepage from './containers/Home'; //this container is created below

function App() {
    return (
        <div className="App">
            <Homepage /> // our imported Homepage container
        </div>
    );
}

export default App;

Now cd into containers and create a file that we will call Home.js.

I'm gonna do something I don't normally like to do...i'm gonna paste the entire file and add comments to each segment rather than break this up. I believe this will be easier to digest in the long run.

// uix/src/containers/Home.js

// our imports - we need react, a future Home.scss file, axios for our api calls, and future components called Forms/List.

import React from 'react';
import './Home.scss';
import axios from 'axios';
import Forms from '../components/Forms';
import List from '../components/List'

// We declare the Hompage class and extend Component to make sure we have state.

class Homepage extends React.Component {
    state = {
        products: [], // products is an empty array in state that we will populate with our api get call below.
        title: '', // title, description, and price are state placeholders for fields in our form
        description: '',
        price: '$'
    }

    // DRF_URL env var is optional - if you use this app as a docker image or kube container you can easily pass an env var with a different api url. Otherwise this will default to localhost

    DRF_URL = process.env.DRF_URL || 'http://localhost:8000'

    componentDidMount() {

        // on mount, the axios api call will call out to our api with a get() method

        axios.get(this.DRF_URL + '/api/')
            .then(res => {

                //then, it will log the data to console and set the state of our products array to the response data.

                console.log(res.data);

                this.setState({
                    products: res.data
                });
            });
    }

    // This onChange function takes an event parameter and then sets the state to the event target name/value (see the Forms app later in this post)

    onChange = (e) => {
        this.setState({ [e.target.name]: e.target.value });
    }

    render() {
        // this function takes an event and then sets our Form values to state. Then the axios post() method posts those vaules to our api and into our database.

        const onSubmit = (e) => {
            const { title, description, price } = this.state;
            axios.post(this.DRF_URL + '/api/', { title, description, price })
                .then((result) => {
                });
        }

        // Delete calls axios delete() on our api's detail view with the id from the mapped element. it also logs a confirmation to our console and reloads the page

        const Delete = (id) => {
            console.log("this is deleted")
            axios.delete(this.DRF_URL + '/api/' + id, { id })
                .then((result) => {
                    window.location.reload(true);
                });
        }
        // and finally we have our JSX with the Forms (passing onSubmit, onChange, title, description, and price as props. And List with products array and delete function as props.

        return (
            <div className="homepage">
                <Forms
                    onSubmit={onSubmit}
                    onChange={this.onChange}
                    title={this.state.title}
                    description={this.state.description}
                    price={this.state.price}
                />
                <List
                    products={this.state.products}
                    delete={Delete}
                />
            </div>
        )
    }
}
//export the Homepage class

export default Homepage;

I also made a Home.scss file in the same directory. Feel free to copy this, but I don't want to focus css specific in this post.

uix/src/containers/Home.scss

.homepage {
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    width: 50%;
    margin: 0 auto;
    .add {
        width: 100%;
        margin: 0 auto;
        form {
            width: 50%;
            display: flex;
            flex-direction: column;
            margin: 0 auto;
            input {
                margin: 1rem 0;
                height: 2rem;
            }
        }
    }
    .list-items {
        display: grid;
        grid-template-columns: auto auto auto;
        grid-column-gap: 5rem;
        grid-row-gap: 5rem;
        .product-tile {
            padding: 2rem 5rem;
            border: 1px solid gray;
            border-radius: 5px;
            box-shadow: 0px 0px 20px lightgrey;
        }
    }
}

 

Now lets cd back into our components and finish this functionality off with our forms and list that will take props from our Home container.

 

// uix/src/components/Forms.js
import React from 'react';

// Fewer things to note in this file. Forms function takes "props" as a parameter passed from Home container.
const Forms = (props) => {
    // We return JSX
    return (
        <div className="add">
            <h1>Add a Product </h1>

// notes the {props.x} in each section here. These are the values we passed above.

            <form onSubmit={props.onSubmit}>
                <input name="title" type="text" placeholder="Enter Title" value={props.title} onChange={props.onChange} />
                <input name="description" type="text" placeholder="Enter Description" value={props.description} onChange={props.onChange} />
                <input name="price" type="text" placeholder="Enter Price" value={props.price} onChange={props.onChange} />
                <input type="submit" />
            </form>
        </div>
    )
}

export default Forms;

and then List.js which will show our query results.

// uix/src/components/List.js

import React from 'react';

// Same here as above. List takes props as a function.

const List = (props) => {
    return (
        <div className="list-items">

// Here we are taking the products prop and mapping it to individual elements in this component.

            {props.products.map(product => {
                return (
                    <div className="product-tile">
                        <p>{product.title}</p>
                        <p>{product.description}</p>
                        <p>{product.price}</p>

// when clicking the delete button, it calls the delete function in the Home container and passes the product id from the mapped array

                        <button onClick={() => { props.delete(product.id) }}>delete</button>
                    </div>
                )
            })}
        </div>
    )
}

export default List;

And that's it for the primary section!

to run this locally, head back to uix and run

npm start

 

Extra Curricular

Here's my docker file if you wanted a reference!

FROM node:12.13.1

WORKDIR /app

ENV PATH /app/node_modules/.bin:$PATH

COPY package.json /app/

COPY package-lock.json /app/

RUN npm install --silent

RUN npm install react-scripts@3.4.1 -g --silent


COPY . /app/


CMD ["npm", "start"]

And that's all i've got! Look for a video to drop soon with a walkthrough. As always subscribe and like the youtube channel if you are enjoying the content.

Until next time,

Scotty