Building a contact app from scratch with React.js

If you are new to React.js my recent tutorial at https://app.wftutorials.com/ shows you how you can create a contacts application with react. I will go through some of the basics here but you can find the full walk-through here. I also did a video on it check that out here.

Lets get started.

The app I will be making has two main sections as shown in the picture below.

Sample picture of the app

The File Structure

The are a few directory to take note.

  • The public directory contains our main html template.
  • The src directory contains our source JavaScript that defines how our application works.
  • In the src directory we can pay attention to the App.js, home.js, contacts.js, create-contact.js and view-contact.js files.

You can find these files here. This is also the source github repo for our project.

Displaying our Data

In the picture above we can see two sections. We are now focusing on the right side. This is contacts.js. Here we pull all our contacts within this component and display it to the user. Look at the full code here.

The first thing we do is get our contacts calling the getContacts function.

    constructor(props) {
        super(props);
        this.state = {items:this.props.contacts};
        this.getContacts();
    }

Within the getContacts function we pull data from our server.

    getContacts = () => {
        fetch('http://dev.samples.com/getcontacts.php')
            .then(rsp=>rsp.json())
            .then(response =>{
                this.setState({items:response.data});
                console.log(response.data)
            })
    }

The getcontacts.php is a simple file that returns records from a mysql database. A sample of this file is shown below.

try {
    $conn = new PDO($dsn, $user, $passwd);
    if($id){
            $query = $conn->prepare("SELECT * from contacts where id=?");
            $query->execute([$id]);
           $results = $query->fetchAll();
    }else{
        $results = $conn->query("SELECT * from contacts order by id desc");   
    }
    foreach ($results as $result) {
        array_push($data,[
            "id" => $result["id"],
            "name" => !empty($result["name"]) ? $result["name"] : "",
            "email" => !empty($result["email"]) ? $result["email"] : "",
            "phone" => !empty($result["phone"]) ? $result["phone"] : "",
            "notes" => !empty($result["notes"]) ? $result["notes"] : ""
        ]);
    }
} catch (Exception $e) {
    echo $e->getMessage();
}


echo json_encode(['data'=>$data]);

In our getContacts function we set the state to the response of our request.

    this.setState({items:response.data});

So now the state is updated and we can display our records. We use the map function to iterate through our contacts within the render function.

     {this.state.items.map(listitem => (
                        <li key={listitem.id} className="list-group-item">
                            <span onClick={()=>{this.props.viewContact(listitem)}}>
                                <i className="fa fa-user"></i> {listitem.name}</span>
                            <span>
                                {listitem.email ? " | " + listitem.email  : ''}</span>
                            <button onClick={()=>{this.props.editContact(listitem)}} className="btn btn-primary btn-sm float-right">Edit</button>
                        </li>
                    ))}

Add a new Contact

To add a new contact we need to pay attention to the create-contact.js file. This is the component where we handle this process.

The render function of this component is important. Take note of the this.handleChange function that is attached to the onChange handler.

      <form onSubmit={this.handleSubmit}>
                <input name="id" type="hidden" className="form-control" value={this.currentid}/>

                <div className="form-group">
                    <label htmlFor="exampleInputEmail1">Contact Name</label>
                    <input name="name" type="text" className="form-control"
                           onChange={this.handleChange} placeholder="Enter contact name" value={this.state.currentContact.name} required/>
                </div>
                <div className="form-group">
                    <label htmlFor="exampleInputPassword1">Email</label>
                    <input name="email" className="form-control" value={this.state.currentContact.email}
                           onChange={this.handleChange} placeholder="email@test.com"/>
                </div>
     
                <button type="submit" className="btn btn-primary">Save contact</button>
            </form>

In our component we deal with these. The handleChange function ensures our states are always updated with the correct form values.

   handleChange(e){
       if(e.target.name == 'name'){
           this.setState({currentContact : { name: e.target.value}})
       }
       if(e.target.name == 'email'){
           this.setState({currentContact : { email: e.target.value}})
       }
       if(e.target.name == 'phone'){
           this.setState({currentContact : { phone: e.target.value}})
       }
       if(e.target.name == 'notes'){
           this.setState({currentContact: {notes: e.target.value}});
       }
        if(e.target.name == 'id'){
            if(e.target.value != undefined || e.target.value != ''){
                this.setState({currentContact : { id: e.target.value}});
                console.log(e.target.value);
            }
        }
    }

In our component constructor we need to bind these functions so we can access them within our render function.

    constructor(props) {
        super(props);
        this.state = {currentContact: this.props.currentContact};
        this.currentContact = this.props.currentContact;
        this.currentid = this.props.currentContact.id;
        this.handleSubmit = this.handleSubmit.bind(this);
        this.handleChange = this.handleChange.bind(this);
    }

When we submit this form we call the handleSubmit function. This function sends the data to a saveContact function which uploads our data to the server and saves it to a database.

    handleSubmit(event) {
        let formData = new FormData(event.target);
        if (!event.target.checkValidity()) {
            return;
        }
        this.props.saveContact(formData);
        event.preventDefault();
        event.target.reset();
        this.clearCurrentContact();
    }

The this.props.saveContact indicates this function is being passed to the component. Lets see where this is happening.

Our home.js is the main component or our parent component. Within it we can see our saveContact function.

 saveContact(data){
        fetch("http://dev.samples.com/insertcontacts.php",
            {
                body: data,
                method: "post"
            }).then(()=>{  this.onContactAdded() });
    }

Above we use the fetch function specifying the method type as post to send our data.

Parent and child components.

In my original article I went through the relationship of our components with each other. This is important to understand where some functions are created and where they are begin accessed. Building this application most of the functions we registered within home.js component. Then I passed them in between the child components where necessary.

Component relationship in the contacts manager app

Lets just look at home.js to see this happening. At the top of the file we import all the components we need.

import React, {Component} from 'react';
import Contacts from './contacts';
import CreateContacts from './create-contact';
import ContactView from "./view-contact";

Then we render these as we see fit. The contacts component often stays the same. But we switch between viewing a contact and creating a contact components.

How to render components

Rendering a component is pretty easy after you import them. You can use them as html elements using the same name you imported them as.

To render the ContactView we simple add this code below in the render function

     <ContactView currentContact={this.state.currentContact}/>

Its that simple. CurrentContact is a prop that is passed from the main component to our ContactView so within ContactView we can access currentContact using this.props.CurrentContact.

We render the edit contact view in a similar fashion.

 <Contacts editContact={this.editContact}
                              viewContact={this.viewContact}
                              refresh={this.state.refresh}
                              contacts={this.state.items}/>

In the above example you can see we are passing a lot of different props. Within the Contacts component we have access to these variables and functions.

Conditionally Rendering Components

In our Home component. We need to show two different views in two different conditions. So we conditionally render the correct view based on the state of our app. Let see how we accomplish this.

Conditionally rendering views

Above the variable isView determines if we show a view or not. The above flowchart shows the logic that defines which component is being displayed on the left of the app. Let see what the code looks like

Rendering a view conditionally

Conclusion

This is just a little of what React can do. Thanks for reading. You can view the full tutorial here – https://app.wftutorials.com/tutorial/148

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s