Home PHP

React CRUD Tutorial – JavaScript Programming – Step By Step Guide!

react-crud-tutorial-1

Previously, we learned our jQuery AJAX CRUD tutorial. Today we will learn how to create, read, update, and delete database records with React.

Welcome to the fifth part of our CRUD tutorial series! I’m really happy that you’re here. I appreciate your time and support on our website!

I highly recommend studying the previous tutorials first before proceeding here. But if you think you can take this one, then go on.

Our tutorial for today will cover the following contents:

1.0 Program Output
2.0 React: What, Why, Who and Think
3.0 Technologies Used In This Tutorial
4.0 File Structure

5.0 Prepare The Database
5.1 Create the Database
5.2 Create the Database Table
5.3 Dump Sample Data On The Table
5.4 Create Database Connection PHP File

6.0 Basic File Requirements
6.1 Create core.php
6.2 Download Bootstrap
6.3 Download jQuery
6.4 Create layout_head.php
6.5 Create layout_foot.php
6.6 Create index.php
6.7 Output

7.0 Create Record with React
7.1 Initialize Virtual DOM
7.2 Create MainApp Component
7.3 Create Product Component
7.4 Prepare Component State
7.5 Request List Of Categories
7.6 Create read_all_categories.php
7.7 Object Method To Read All Categories
7.8 Stop Category List Request
7.9 Handle State Changes
7.10 Handle Form Submit
7.11 Create create_product.php
7.12 Create Product Class
7.13 HTML Form To Create Product
7.14 Output

8.0 Read Records with React
8.1 MainApp Initial State
8.2 Put Custom changeAppMode Method
8.3 Add App Mode
8.4 Add ReadProductsComponent
8.5 Create read_all_products.php
8.6 Object Code To Real All Products
8.7 Add TopActionsComponent
8.8 Add ProductsTable Component
8.9 Add ProductRow Component
8.10 Output

9.0 Read One Record with React
9.1 Change MainApp Render Method
9.2 Add ReadOneProductComponent
9.3 PHP Script To Read One Product
9.4 Object Method To Read One Product
9.5 Output

10.0 Update Record with React
10.1 Change MainApp Render Method
10.2 Add UpdateProductComponent
10.3 Prepare Component State
10.4 Prepare componentDidMount Method
10.5 Prepare componentWillUnmount Method
10.6 Handle State Changes
10.7 Handle Form Submit
10.8 PHP Script To Update Product
10.9 Object Method To Update Product
10.10 Update Product Form
10.11 Output

11.0 Delete Record with React
11.1 Change MainApp Render Method
11.2 Add DeleteProductComponent
11.3 Create delete_product.php
11.4 Object Method To Delete A Product
11.5 Output

12.0 Download Source Codes
13.0 What’s Next?
14.0 Related Source Codes
15.0 Online Resources
16.0 Some Notes

1.0 Program Output – React CRUD Tutorial

We usually have three LEVELS of source code output. But WHY? Because I believe in "Learning Progression" to ensure efficient learning. Learn more

1.1 LEVEL 1 Source Code Output

1.2 LEVEL 2 Source Code Output

2.0 React: What, Why, Who and Think

2.1 What is React?

React is a JavaScript library for building user interfaces. It is maintained by Facebook, Instagram and a community of individual developers and corporations.

2.2 Why use React?

Using React allows you to simply express how your app should look at any given point in time. React will automatically manage all UI updates when your underlying data changes. When the data changes, React conceptually hits the “refresh” button, and knows to only update the changed parts.

2.3 Who uses React?

Aside from Facebook and Instagram, many well-known companies uses React including: Dropbox, PayPal, Nextflix, Khan Academy, Airbnb, Alipay, Asana and many more.

2.4 Thinking in React

It is very important that we understand the thought process of building web apps with React. Here’s a start: We start with a mock. Then, we break the UI into a component or parts of the UI. In the following image, you can see the mock and components in colored boxes.

thinking-in-react-components

I will not discuss this topic longer here. To learn more about how to think in React, click here.

Another point: Before, we code HTML and JavaScript separately on a web page. In React, we code HTML inside JavaScript. This is made possible through JSX. To learn more about JSX, click here.

React concepts can be hard to grasp at first, but you will get there. If you do actual coding with React, you will understand it better and faster. This is why our step-by-step React CRUD tutorial below will be very useful.

3.0 Technologies Used In This Tutorial

Here we will learn how to create, read update and delete database records – CRUD operations with PHP, MySQL, React, Babel, jQuery and Bootstrap.

Why do I use these technologies for this React CRUD tutorial? Here are my reasons:

  1. They work.
  2. Most audience of our blog are very familiar with these technologies.
  3. The goal of this tutorial is for you to learn how to use React.
  4. React is only the V in MVC.
  5. React don’t make assumptions about the rest of your technology stack.

Now, let’s take a look at the roles of each technology that we will use:

  • PHP – will handle server side script.
  • MySQL – will store our data.
  • React – will make our UI fast and interactive.
  • Babel – will compile our JavaScript so we don’t have to wait for browser support.
  • jQuery – will do AJAX requests. React official docs shows jQuery examples.
  • Bootstrap – will make our UI look better.

For those who ask, React can work with AngularJS. In short, it can work with the MEAN (mongoDB, Express, AngularJS and NodeJS) framework. We will have another tutorial for that soon! Please subscribe so I can notify you when it is done.

4.0 File Structure

It is useful to see the final file structure of this React CRUD tutorial. This way, we can easily visualize where we are going. You do not need to memorize all the files or folders. You just have to understand them.

Folders and files are in bold characters. I put a small description of each files and folders. Once we complete the tutorial, we will have the file structure that looks like the following.

├─api/ – contains PHP files that works with the server.
├───create_product.php – will insert data to MySQL database.
├───delete_products.php – will delete data from MySQL database.
├───read_all_categories.php – will select categories from MySQL database.
├───read_all_products.php – will select products from MySQL database.
├───read_one_product.php – will select one product from MySQL database.
├───update_product.php – will update a product from MySQL database.
├─config/ – contains PHP files for settings.
├───core.php – here we can set error reporting and timezone.
├───database.php – will connect us to the MySQL database.
├─dev/ – contains SQL file and notes for developers.
├───db.sql – import this file to your MySQL database.
├───readme.txt – important notes for developer.
├─libs/ – contains useful libraries for our app.
├───css/ – contains libraries related to styling.
├─────bootstrap/ – makes our app look good. [Learn more]
├───js/ – contains libraries related to JavaScript.
├─────react/ – contains our React script.
├───────main.js – our React script can be edited in this file.
├─────jquery.js – library that will help us with AJAX requests.
├─objects/ – contains PHP libraries or objects
├─────product.php – object related to products database table
├─────category.php – object related to categories database table
├─index.php – is where our single page app (SPA) will load.
├─info.php – for debugging purposes.
├─layout_foot.php – contains HTML and JS on the lower part of index.php
├─layout_head.php – contains HTML and CSS on the upper part of index.php

5.0 Prepare The Database

I assume you are using PhpMyAdmin to manage the database. If not, contact me.

5.1 Create the Database

First, create a database with a name “php_react_crud”.

5.2 Create the Database Tables

Run the following SQL to create the products and categories table.

-- Table structure for table `products`
CREATE TABLE IF NOT EXISTS `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  `description` text NOT NULL,
  `price` double NOT NULL,
  `category_id` int(11) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=35 ;

-- Table structure for table `categories`
CREATE TABLE IF NOT EXISTS `categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  `description` text NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

5.3 Dump Sample Data On The Table

Use the following SQL query to insert sample data.

-- Dumping data for table `products`
INSERT INTO `products` (`id`, `name`, `description`, `price`, `category_id`, `created`, `modified`) VALUES
(1, 'Basketball', 'A ball used in the NBA.', 49.99, 1, '2015-08-02 12:04:03', '2015-08-02 15:57:13'),
(3, 'Gatorade', 'This is a very good drink for athletes.', 1.99, 1, '2015-08-02 12:14:29', '2015-08-02 15:57:13'),
(4, 'Eye Glasses', 'It will make you read better.', 6, 1, '2015-08-02 12:15:04', '2015-08-02 15:57:13'),
(5, 'Trash Can', 'It will help you maintain cleanliness.', 3.95, 1, '2015-08-02 12:16:08', '2015-08-02 15:57:13'),
(6, 'Mouse', 'Very useful if you love your computer.', 11.35, 2, '2015-08-02 12:17:58', '2015-08-02 15:57:38'),
(7, 'Earphone', 'You need this one if you love music.', 9, 2, '2015-08-02 12:18:21', '2016-07-20 14:56:22'),
(8, 'Pillow', 'Sleeping well is important.', 8.99, 2, '2015-08-02 12:18:56', '2015-08-02 15:57:38'),
(13, 'Cellphone Stand', 'Very useful if you are a developer.', 5.55, 2, '2015-08-03 08:00:16', '2016-07-20 14:56:17');

-- Dumping data for table `categories`
INSERT INTO `categories` (`id`, `name`, `description`, `created`, `modified`) VALUES
(1, 'Sports', 'Products you use for sports.', '2015-08-02 23:56:46', '2015-08-03 05:59:36'),
(2, 'Personal', 'Products for you personal needs.', '2015-08-02 23:56:46', '2015-08-03 05:59:36');

5.4 Create Database Connection PHP File

Create the “config” folder and “database.php” file inside that folder. The database.php file will have the following code:

<?php
class Database{

	// specify your own database credentials
	private $host = "localhost";
	private $db_name = "php_react_crud";
	private $username = "root";
	private $password = "";
	public $conn;

	// get the database connection
	public function getConnection(){

		$this->conn = null;

		try{
			$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
		}catch(PDOException $exception){
			echo "Connection error: " . $exception->getMessage();
		}

		return $this->conn;
	}
}
?>

Good job preparing the database for this React CRUD tutorial! Let’s achieve more output by proceeding to the next sections below.

6.0 Basic File Requirements

6.1 Create core.php

Create core.php file inside the config folder. This file contains other app settings such as error reporting or timezone settings.

This is not required but this is a good thing to practice. Put the following code inside it.

<?php
// show error reporting
error_reporting(E_ALL);

// set your default time-zone
date_default_timezone_set('Asia/Manila');
?>

6.2 Download Bootstrap

We use Bootstrap to make our user interface look good. If you’re not yet familiar with it, please learn from our Bootstrap tutorial for beginners

Put the bootstrap files inside “libs/css/” directory.

6.3 Download jQuery

We use jQuery for AJAX requests. If you’re not yet familiar with it, please learn from our jQuery tutorial for beginners

Put the jQuery library file inside “libs/js/” directory.

6.4 Create layout_head.php

Create the layout_head.php file. This file will hold our page’s header HTML and CSS. Put the following code inside it.

<!DOCTYPE html>
<html lang="en">
<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>PHP CRUD with ReactJS</title>

    <!-- Bootstrap CSS -->
	<link href="libs/css/bootstrap/dist/css/bootstrap.css" rel="stylesheet" media="screen">

    <!-- HTML5 Shiv and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
    <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->

	<style>
	.text-align-center{
		text-align:center;
	}

	.margin-zero{
		margin:0;
	}

	.overflow-hidden{
		overflow:hidden;
	}

	.margin-bottom-1em{
		margin-bottom:1em;
	}

	.m-r-1em{
		margin-right:1em;
	}
	</style>

</head>
<body>

    <!-- container -->
    <div class="container">

        <div class="page-header">
            <h1>Loading...</h1>
        </div>

6.5 Create layout_foot.php

This time, create the layout_foot.php file. This file will hold our page footer’s HTML and JavaScript. Put the following code inside it.

	</div>
	<!-- /container -->

<!-- react js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>

<!-- main react components -->
<script type="text/babel" src="libs/js/react/main.js"></script>

<!-- jQuery library -->
<script src="libs/js/jquery.js"></script>

<!-- bootstrap JavaScript -->
<script src="libs/css/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="libs/css/bootstrap/docs-assets/js/holder.js"></script>

</body>
</html>

As you may have noticed, we used the CloudFlare CDN to load React and Babel in our project. This will save us some extra steps.

6.6 Create index.php

Create index.php file with the following code. The index.php is where our app will load.

<?php
// include configuration file
include 'config/core.php';

// include the head template
include_once "layout_head.php";

// placeholder for rendering React components
echo "<div id='content'></div>";

// page footer
include_once "layout_foot.php";
?>

6.7 Output

Here’s the output of all we did in section 6 of this React CRUD tutorial.
level-1-index-page

It is almost like a blank page. But don’t worry! We will add awesome features, let’s continue to code by proceeding to the next sections.

7.0 Create Record with React

7.1 Initialize Virtual DOM

Create main.js file and put it inside “libs/js/react/” directory. This file will hold all our React scripts. Put the following code inside main.js, this will initialize our React DOM, also called Virtual DOM.

ReactDOM.render(
    <MainApp />,
    document.getElementById('content')
);

7.2 Create MainApp Component

As you can see in the previous section, the MainApp tag was called. Now, we will create a React component class for that. Put the following code above the previous section’s code.

var MainApp = React.createClass({
    render: function() {
        return <CreateProductComponent />;
    }
});

7.3 Create Product Component

The CreateProductComponent was returned in the previous section’s code. This component will contain the logic to create a new record. Put the following code above the previous section’s code.

var CreateProductComponent = React.createClass({

});

7.4 Prepare Component State

State is reserved only for interactivity or data that changes overtime. State is invoked once before the component is mounted.

On our case, we’ll have the product categories, fields and display data. Put the following code inside the createClass function of the previous section.

getInitialState: function() {
    return {
        categories: [],
        selectedCategoryId: -1,
        name: '',
        description: '',
        price: '',
        successCreation: null
    };
},

7.5 Request List Of Categories

The componentDidMount method is invoked once immediately after the initial rendering occurs. This is where we usually perform AJAX requests. In our case, we will get the list of categories from the database using a jQuery AJAX request.

We change the page title to ‘Create Product’ as well. Put the following code right after the getInitialState method of the previous section.

componentDidMount: function() {
    this.serverRequest = $.get("api/read_all_categories.php", function (categories) {
        this.setState({
            categories: JSON.parse(categories)
        });
    }.bind(this));

    $('.page-header h1').text('Create product');
},

7.6 Create read_all_categories.php

The previous section won’t work without the read_all_categories.php file. Create read_all_categories.php file inside the “api” folder and put the following code inside it.

<?php
// include core configuration
include_once '../config/core.php';

// include database connection
include_once '../config/database.php';

// product object
include_once '../objects/category.php';

// class instance
$database = new Database();
$db = $database->getConnection();
$category = new Category($db);

// read all products
$results=$category->readAll();

// output in json format
echo $results;
?>

7.7 Object Method To Read All Categories

The previous section will not work without the readAll() object method. Create “category.php” inside the “objects” folder and put the following code.

<?php
class Category{

	// database connection and table name
	private $conn;
	private $table_name = "categories";

	// object properties
	public $id;
	public $name;

	public function __construct($db){
		$this->conn = $db;
	}

	public function readAll(){

		//select all data
		$query = "SELECT id, name FROM categories ORDER BY name";

		$stmt = $this->conn->prepare($query);
		$stmt->execute();

		$categories=$stmt->fetchAll(PDO::FETCH_ASSOC);

		return json_encode($categories);
	}

}
?>

7.8 Stop Category List Request

The componentWillUnmount method is invoked immediately before a component is unmounted from the DOM. This is where we perform any necessary cleanup, such as invalidating timers or cleaning up any DOM elements that were created in componentDidMount.

In our case, we use this method to stop getting categories data – in case the request is still loading. This is a very useful feature to make our app run smoothly. Put the following code under the code in section 7.5 above.

componentWillUnmount: function() {
    this.serverRequest.abort();
},

7.9 Handle State Changes

This is how we handle changes in our state. When state changes, it means there is a change in our form input as well. We will create custom methods bound to each fields in our form. They are like ‘on change’ listeners. We will see how this is integrated with our form later. For now, put the following codes after the code on the previous section.

// handle category change
onCategoryChange: function(e) {
    this.setState({selectedCategoryId: e.target.value});
},

// handle name change
onNameChange: function(e) {
    this.setState({name: e.target.value});
},

// handle description change
onDescriptionChange: function(e) {
    this.setState({description: e.target.value});
},

// handle price change
onPriceChange: function(e) {
    this.setState({price: e.target.value});
},

7.10 Handle Form Submit

Here’s our custom method that is executed once the ‘create product’ form was submitted. We use jQuery AJAX to post the data to create_product.php file. We empty the fields once the response returned is ‘true’. Put the following codes after the previous section’s code.

onSave: function(e){
    $.post("api/create_product.php", {
            name: this.state.name,
            description: this.state.description,
            price: this.state.price,
            category_id: this.state.selectedCategoryId
        },
        function(res){
            this.setState({successCreation: res});
            this.setState({name: ""});
            this.setState({description: ""});
            this.setState({price: ""});
            this.setState({selectedCategoryId: -1});
        }.bind(this)
    );
    e.preventDefault();
},

7.11 Create create_product.php

Create create_product.php inside the “api” folder. This PHP script will accept the posted data from the previous section. Put the following code inside create_product.php

<?php
// if the form was submitted
if($_POST){

	// include core configuration
	include_once '../config/core.php';

	// include database connection
	include_once '../config/database.php';

	// product object
	include_once '../objects/product.php';

	// class instance
	$database = new Database();
	$db = $database->getConnection();
	$product = new Product($db);

	// set product property values
	$product->name = $_POST['name'];
	$product->price = $_POST['price'];
	$product->description = $_POST['description'];
	$product->category_id = $_POST['category_id'];

	// create the product
	echo $product->create() ? "true" : "false";
}
?>

7.12 Create Product Class

The previous section will not work without the product.php file. This file holds our product object class where we can call useful methods to perform tasks us create, read, update and delete products.

Create the “objects” folder and create the “product.php” file inside it. Open product.php and put the following code.

<?php
class Product{

	// database connection and table name
	private $conn;
	private $table_name = "products";

	// object properties
	public $id;
	public $name;
	public $price;
	public $description;
	public $category_id;
	public $timestamp;

	public function __construct($db){
		$this->conn = $db;
	}

    public function create(){
        try{

            // insert query
            $query = "INSERT INTO products
                SET name=:name, description=:description, price=:price, category_id=:category_id, created=:created";

            // prepare query for execution
            $stmt = $this->conn->prepare($query);

            // sanitize
            $name=htmlspecialchars(strip_tags($this->name));
            $description=htmlspecialchars(strip_tags($this->description));
            $price=htmlspecialchars(strip_tags($this->price));
            $category_id=htmlspecialchars(strip_tags($this->category_id));

            // bind the parameters
            $stmt->bindParam(':name', $name);
            $stmt->bindParam(':description', $description);
            $stmt->bindParam(':price', $price);
            $stmt->bindParam(':category_id', $category_id);

            // we need the created variable to know when the record was created
            // also, to comply with strict standards: only variables should be passed by reference
            $created=date('Y-m-d H:i:s');
            $stmt->bindParam(':created', $created);

            // Execute the query
            if($stmt->execute()){
                return true;
            }else{
                return false;
            }

        }

        // show error if any
        catch(PDOException $exception){
            die('ERROR: ' . $exception->getMessage());
        }
    }
}

7.13 HTML Form To Create Product

The following render method code does the following.

  • Arrange ‘options’ for our categories drop down. (related to section 7.5)
  • Tell the user if a product was created.
  • Tell the user if unable to create product.
  • Button to go back to products list.
  • Form to create a product.
  • Binds each field to custom onChange methods. (related to section 7.6)

Put the following code under section 7.10’s code.

render: function() {

    // make categories as option for the select tag.
    var categoriesOptions = this.state.categories.map(function(category){
        return (
            <option key={category.id} value={category.id}>{category.name}</option>
        );
    });

    /*
    - tell the user if a product was created
    - tell the user if unable to create product
    - button to go back to products list
    - form to create a product
    */
    return (
    <div>
        {

            this.state.successCreation == "true" ?
                <div className='alert alert-success'>
                    Product was saved.
                </div>
            : null
        }

        {

            this.state.successCreation == "false" ?
                <div className='alert alert-danger'>
                    Unable to save product. Please try again.
                </div>
            : null
        }

        <a href='#'
            onClick={() => this.props.changeAppMode('read')}
            className='btn btn-primary margin-bottom-1em'> Read Products
        </a>


        <form onSubmit={this.onSave}>
            <table className='table table-bordered table-hover'>
            <tbody>
                <tr>
                    <td>Name</td>
                    <td>
                        <input
                        type='text'
                        className='form-control'
                        value={this.state.name}
                        required
                        onChange={this.onNameChange} />
                    </td>
                </tr>

                <tr>
                    <td>Description</td>
                    <td>
                        <textarea
                        type='text'
                        className='form-control'
                        required
                        value={this.state.description}
                        onChange={this.onDescriptionChange}>
                        </textarea>
                    </td>
                </tr>

                <tr>
                    <td>Price ($)</td>
                    <td>
                        <input
                        type='number'
                        step="0.01"
                        className='form-control'
                        value={this.state.price}
                        required
                        onChange={this.onPriceChange}/>
                    </td>
                </tr>

                <tr>
                    <td>Category</td>
                    <td>
                        <select
                        onChange={this.onCategoryChange}
                        className='form-control'
                        value={this.state.selectedCategoryId}>
                        <option value="-1">Select category...</option>
                        {categoriesOptions}
                        </select>
                    </td>
                </tr>

                <tr>
                    <td></td>
                    <td>
                        <button
                        className='btn btn-primary'
                        onClick={this.onSave}>Save</button>
                    </td>
                </tr>
                </tbody>
            </table>
        </form>
    </div>
    );
}

7.14 Output

You should have the following output so far in this React CRUD tutorial.

level-1-create-record

8.0 Read Records with React

We will now have two screens, we call it ‘mode’ in this tutorial. One for the “create product” and one for the “read product”.

8.1 MainApp Initial State

Now we will put values to MainApp component’s initial state. Put the following code inside the MainApp component class (and before the render method code) in section 7.2 above.

getInitialState: function() {
    return {
        currentMode: 'read',
        productId: null
    };
},

8.2 Put Custom changeAppMode Method

The custom changeAppMode method will help set the current mode of our app, in which we currently have two modes: “create” and “read” products. Put the following code after the previous section’s code.

changeAppMode: function(newMode, productId){
    this.setState({currentMode: newMode});

    if(productId !== undefined){
        this.setState({productId: productId});
    }
},

8.3 Add App Mode

We will change the code on section 7.2 above. We will have ReadProductsComponent as the default component. We put the switch statement there to identify the current mode of our app.

We will add the “readOne”, “update” and “delete” mode on the next few parts of our tutorial. For now, the render method (section 7.2 code) will look like the following:

render: function() {

    var modeComponent =
        <ReadProductsComponent
        changeAppMode={this.changeAppMode} />;

    switch(this.state.currentMode){
        case 'read':
            break;
        case 'create':
            modeComponent = <CreateProductComponent changeAppMode={this.changeAppMode}/>;
            break;
        default:
            break;
    }

    return modeComponent;
}

8.4 Add ReadProductsComponent Class

By now, you should know what methods our react component class usually contains: getInitialState, componentDidMount, componentWillUnmount and render method.

Our ReadProductsComponent will look like the following. You can read the code comments to learn what this component does, it should be easy to understand. If not, ask via the comments section below or email me mike@codeofaninja.com, I will answer your question.

Put the following code above the MainApp (section 7.2) component.

// component that contains all the logic and other smaller components
// that form the Read Products view
var ReadProductsComponent = React.createClass({
    getInitialState: function() {
        return {
            products: []
        };
    },

    // on mount, fetch all products and stored them as this component's state
    componentDidMount: function() {
        this.serverRequest = $.get("api/read_all_products.php", function (products) {
            this.setState({
                products: JSON.parse(products)
            });
        }.bind(this));
    },

    // on unmount, kill product fetching in case the request is still pending
    componentWillUnmount: function() {
        this.serverRequest.abort();
    },

    render: function() {
        // list of products
        var filteredProducts = this.state.products;
        $('.page-header h1').text('Read Products');

        return (
            <div className='overflow-hidden'>
                <TopActionsComponent changeAppMode={this.props.changeAppMode} />

                <ProductsTable
                    products={filteredProducts}
                    changeAppMode={this.props.changeAppMode} />
            </div>
        );
    }
});

8.5 Create read_all_products.php

The previous section won’t work without read_all_products.php that gets data from the database in JSON format. Inside “api” folder, create read_all_products.php with the following code.

<?php
// include core configuration
include_once '../config/core.php';

// include database connection
include_once '../config/database.php';

// product object
include_once '../objects/product.php';

// class instance
$database = new Database();
$db = $database->getConnection();
$product = new Product($db);

// read all products
$results=$product->readAll();

// output in json format
echo $results;
?>

8.6 Object Code To Real All Products

The previous section will not work without the readAll() object method. Add the following code below the create() method of our “product.php” inside the “objects” folder.

public function readAll(){

    //select all data
    $query = "SELECT p.id, p.name, p.description, p.price, c.name as category_name
    			FROM " . $this->table_name . " p
    				LEFT JOIN categories c
    					ON p.category_id=c.id
    			ORDER BY id DESC";

    $stmt = $this->conn->prepare($query);
    $stmt->execute();

    $results=$stmt->fetchAll(PDO::FETCH_ASSOC);

    return json_encode($results);
}

8.7 Add TopActionsComponent

As you can see in the section 8.4, ReadProductsComponent renders two other components: TopActionsComponent and ProductsTable. We will add the TopActionsComponent by putting the following code above ReadProductsComponent.

// component that contains the functionalities that appear on top of
// the products table: create product
var TopActionsComponent = React.createClass({
    render: function() {
        return (
            <div>
                <a href='#'
                    onClick={() => this.props.changeAppMode('create')}
                    className='btn btn-primary margin-bottom-1em'> Create product
                </a>
            </div>
        );
    }
});

8.8 Add ProductsTable Component

ProductsTable component renders our table head and body where the list of data and action buttons (read, edit, delete) will be shown. Put the following code above the TopActionsComponent code of the previous section.

// component for the whole products table
var ProductsTable = React.createClass({
    render: function() {

    var rows = this.props.products
        .map(function(product, i) {
            return (
                <ProductRow
                    key={i}
                    product={product}
                    changeAppMode={this.props.changeAppMode} />
            );
        }.bind(this));

        return(
            !rows.length
                ? <div className='alert alert-danger'>No products found.</div>
                :
                <table className='table table-bordered table-hover'>
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Description</th>
                            <th>Price</th>
                            <th>Category</th>
                            <th>Action</th>
                        </tr>
                    </thead>
                    <tbody>
                        {rows}
                    </tbody>
                </table>
        );
    }
});

8.9 Add ProductRow Component

As you can see in the previous section, ProductsTable component renders the ProductRow component. It renders the single row product record. Add the following code above the previous section’s code.

// component that renders a single product
var ProductRow = React.createClass({
    render: function() {
    return (
        <tr>
            <td>{this.props.product.name}</td>
            <td>{this.props.product.description}</td>
            <td>${parseFloat(this.props.product.price).toFixed(2)}</td>
            <td>{this.props.product.category_name}</td>
            <td>
                <a href='#'
                    onClick={() => this.props.changeAppMode('readOne', this.props.product.id)}
                    className='btn btn-info m-r-1em'> Read
                </a>
                <a href='#'
                    onClick={() => this.props.changeAppMode('update', this.props.product.id)}
                    className='btn btn-primary m-r-1em'> Edit
                </a>
                <a
                    onClick={() => this.props.changeAppMode('delete', this.props.product.id)}
                    className='btn btn-danger'> Delete
                </a>
            </td>
        </tr>
        );
    }
});

8.10 Output

Here’s the output of all we did in section 8 of this React CRUD tutorial. When you clicked the “Create Product” button, it will show you the working form to create a product.

level-1-read-records

9.0 Read One Record with React

9.1 Change MainApp Render Method

We need to add another mode or “case” on our switch statement. It will be the “readOne” case. It will return the ReadOneProductComponent class. Our MainApp render method will now look like the following.

render: function() {

    var modeComponent =
        <ReadProductsComponent
        changeAppMode={this.changeAppMode} />;

    switch(this.state.currentMode){
        case 'read':
            break;
        case 'readOne':
            modeComponent = <ReadOneProductComponent productId={this.state.productId} changeAppMode={this.changeAppMode}/>;
            break;
        case 'create':
            modeComponent = <CreateProductComponent changeAppMode={this.changeAppMode} />;
            break;
        default:
            break;
    }

    return modeComponent;
}

9.2 ReadOneProductComponent Class

Now, here’s the component that will render a single product data on an HTML table.

var ReadOneProductComponent = React.createClass({

    getInitialState: function() {
        // make sure that no other values are set
        return {
            id: 0,
            name: '',
            description: '',
            price: 0,
            category_name: ''
        };
    },

    // on mount, read one product based on given product ID
    componentDidMount: function() {

        var productId = this.props.productId;

        this.serverRequestProd = $.post("api/read_one_product.php",
            {prod_id: productId},
            function (product) {
                var p = JSON.parse(product)[0];
                this.setState({category_name: p.category_name});
                this.setState({id: p.id});
                this.setState({name: p.name});
                this.setState({description: p.description});
                this.setState({price: p.price});
            }.bind(this));

        $('.page-header h1').text('Read Product');
    },

    // on unmount, kill fetching the product data in case the request is still pending
    componentWillUnmount: function() {
        this.serverRequestProd.abort();
    },

    // show single product data on a table
    render: function() {

        return (
            <div>
                <a href='#'
                    onClick={() => this.props.changeAppMode('read')}
                    className='btn btn-primary margin-bottom-1em'>
                    Read Products
                </a>

                <form onSubmit={this.onSave}>
                    <table className='table table-bordered table-hover'>
                        <tbody>
                        <tr>
                            <td>Name</td>
                            <td>{this.state.name}</td>
                        </tr>

                        <tr>
                            <td>Description</td>
                            <td>{this.state.description}</td>
                        </tr>

                        <tr>
                            <td>Price ($)</td>
                            <td>${parseFloat(this.state.price).toFixed(2)}</td>
                        </tr>

                        <tr>
                            <td>Category</td>
                            <td>{this.state.category_name}</td>
                        </tr>

                        </tbody>
                    </table>
                </form>
            </div>
        );
    }
});

9.3 PHP Script To Read One Product

The componentDidMount method in the previous section will not work without read_one_product.php, create that file inside the “api” folder and put the following code inside it.

<?php
// include core configuration
include_once '../config/core.php';

// include database connection
include_once '../config/database.php';

// product object
include_once '../objects/product.php';

// class instance
$database = new Database();
$db = $database->getConnection();
$product = new Product($db);

// read all products
$product->id=$_POST['prod_id'];
$results=$product->readOne();

// output in json format
echo $results;
?>

9.4 Object Method To Read One Product

Add the following object method inside Product class in objects/product.php – this will make our code on previous section work.

public function readOne(){

    // select one record
    $query = "SELECT p.id, p.name, p.description, p.price, p.category_id, c.name as category_name
                FROM " . $this->table_name . " p LEFT JOIN categories c ON p.category_id=c.id
                WHERE p.id=:id";

    //prepare query for excecution
    $stmt = $this->conn->prepare($query);

    $id=htmlspecialchars(strip_tags($this->id));
    $stmt->bindParam(':id', $id);
    $stmt->execute();

    $results=$stmt->fetchAll(PDO::FETCH_ASSOC);

    return json_encode($results);
}

9.5 Output

Here’s the output when the user clicks the “Read” button in the read mode.

level-1-read-one-record

10.0 Update Record with React

10.1 Change MainApp Render Method

Add the ‘update’ case on the switch statement of our MainApp’s render method. The method should look like the following.

render: function() {

    var modeComponent =
        <ReadProductsComponent
        changeAppMode={this.changeAppMode} />;

    switch(this.state.currentMode){
        case 'read':
            break;
        case 'readOne':
            modeComponent = <ReadOneProductComponent productId={this.state.productId} changeAppMode={this.changeAppMode}/>;
            break;
        case 'create':
            modeComponent = <CreateProductComponent changeAppMode={this.changeAppMode}/>;
            break;
        case 'update':
            modeComponent = <UpdateProductComponent productId={this.state.productId} changeAppMode={this.changeAppMode}/>;
            break;
        default:
            break;
    }

    return modeComponent;
}

10.2 Add UpdateProductComponent

This component contains the logic to update a product. It includes server requests to read categories (for dropdown), read single product, update a product, and the update form itself. Add the following code above the MainApp component class.

var UpdateProductComponent = React.createClass({

});

10.3 Prepare Component State

Put the following code inside UpdateProductComponent class of the previous section.

getInitialState: function() {
    return {
        categories: [],
        selectedCategoryId: 0,
        id: 0,
        name: '',
        description: '',
        price: 0,
        successUpdate: null
    };
},

10.4 Prepare componentDidMount Method

On mount, fetch all categories and store them as this component’s state. Read a single record based on given product ID as well.

componentDidMount: function() {
    this.serverRequestCat = $.get("api/read_all_categories.php", function (categories) {
        this.setState({
            categories: JSON.parse(categories)
        });
    }.bind(this));

    var productId = this.props.productId;
    this.serverRequestProd = $.post("api/read_one_product.php",
        {prod_id: productId},
        function (product) {
            var p = JSON.parse(product)[0];
            this.setState({selectedCategoryId: p.category_id});
            this.setState({id: p.id});
            this.setState({name: p.name});
            this.setState({description: p.description});
            this.setState({price: p.price});
        }.bind(this));

    $('.page-header h1').text('Update product');
},

The scripts of read_all_categories.php and read_one_product.php is already done in the previous sections.

10.5 Prepare componentWillUnmount Method

On unmount, kill categories and product fetching in case the request is still pending. Put the following code after the previous section’s code.

componentWillUnmount: function() {
    this.serverRequestCat.abort();
    this.serverRequestProd.abort();
},

10.6 Handle State Changes

Put the following code after the previous sections’s code. This is how we handle form field changes as explained in section 7.9 above.

// handle category change
onCategoryChange: function(e) {
    this.setState({selectedCategoryId: e.target.value});
},

// handle name change
onNameChange: function(e) {
    this.setState({name: e.target.value});
},

// handle description change
onDescriptionChange: function(e) {
    this.setState({description: e.target.value});
},

// handle price change
onPriceChange: function(e) {
    this.setState({price: e.target.value});
},

10.7 Handle Form Submit

The following code is executed when the user clicked the “Save Changes” button. Put it after the previous section’s code.

onSave: function(e){
    $.post("api/update_product.php", {
            id: this.state.id,
            name: this.state.name,
            description: this.state.description,
            price: this.state.price,
            category_id: this.state.selectedCategoryId
        },
        function(res){
            this.setState({successUpdate: res});
        }.bind(this)
    );
    e.preventDefault();
},

10.8 PHP Script To Update Product

The previous section will not work without update_product.php file, let’s create that file inside the “api” folder. The following code must be inside it.

<?php
// if the form was submitted
if($_POST){

	// include core configuration
	include_once '../config/core.php';

	// include database connection
	include_once '../config/database.php';

	// product object
	include_once '../objects/product.php';

	// class instance
	$database = new Database();
	$db = $database->getConnection();
	$product = new Product($db);

	// new values
	$product->name=$_POST['name'];
	$product->description=$_POST['description'];
	$product->price=$_POST['price'];
	$product->category_id=$_POST['category_id'];
	$product->id=$_POST['id'];

	// update the product
	echo $product->update() ? "true" : "false";
}
?>

10.9 Object Method To Update Product

The previous section will not work without the following code on our product class in objects/product.php

public function update(){

    $query = "UPDATE products
                SET name=:name, description=:description, price=:price, category_id=:category_id
                WHERE id=:id";

    //prepare query for excecution
    $stmt = $this->conn->prepare($query);

    // sanitize
    $name=htmlspecialchars(strip_tags($this->name));
    $description=htmlspecialchars(strip_tags($this->description));
    $price=htmlspecialchars(strip_tags($this->price));
    $category_id=htmlspecialchars(strip_tags($this->category_id));
    $id=htmlspecialchars(strip_tags($this->id));

    // bind the parameters
    $stmt->bindParam(':name', $name);
    $stmt->bindParam(':description', $description);
    $stmt->bindParam(':price', $price);
    $stmt->bindParam(':category_id', $category_id);
    $stmt->bindParam(':id', $id);

    // execute the query
    if($stmt->execute()){
        return true;
    }else{
        return false;
    }
}

10.10 Update Product Form

The following render method code does the following.

  • Arrange “options” for our categories drop down. (related to section 10.4)
  • Tell the user if a product was updated.
  • Tell the user if unable to update product.
  • Button to go back to products list.
  • Form to update a product.
  • Binds each field to custom onChange methods. (related to section 10.6)

Put the following code after the code on section 10.7 above.

render: function() {
    var categoriesOptions = this.state.categories.map(function(category){
        return (
            <option key={category.id} value={category.id}>{category.name}</option>
        );
    });

    return (
        <div>
            {
                this.state.successUpdate == "true" ?
                    <div className='alert alert-success'>
                        Product was updated.
                    </div>
                : null
            }

            {
                this.state.successUpdate == "false" ?
                    <div className='alert alert-danger'>
                        Unable to update product. Please try again.
                    </div>
                : null
            }

            <a href='#'
                onClick={() => this.props.changeAppMode('read')}
                className='btn btn-primary margin-bottom-1em'>
                Read Products
            </a>

            <form onSubmit={this.onSave}>
                <table className='table table-bordered table-hover'>
                    <tbody>
                    <tr>
                        <td>Name</td>
                        <td>
                            <input
                                type='text'
                                className='form-control'
                                value={this.state.name}
                                required
                                onChange={this.onNameChange} />
                        </td>
                    </tr>

                    <tr>
                        <td>Description</td>
                        <td>
                            <textarea
                                type='text'
                                className='form-control'
                                required
                                value={this.state.description}
                                onChange={this.onDescriptionChange}></textarea>
                        </td>
                    </tr>

                    <tr>
                        <td>Price ($)</td>
                        <td>
                            <input
                                type='number'
                                step="0.01"
                                className='form-control'
                                value={this.state.price}
                                required
                                onChange={this.onPriceChange}/>
                        </td>
                    </tr>

                    <tr>
                        <td>Category</td>
                        <td>
                            <select
                                onChange={this.onCategoryChange}
                                className='form-control'
                                value={this.state.selectedCategoryId}>
                                <option value="-1">Select category...</option>
                                {categoriesOptions}
                                </select>
                        </td>
                    </tr>

                    <tr>
                        <td></td>
                        <td>
                            <button
                                className='btn btn-primary'
                                onClick={this.onSave}>Save Changes</button>
                        </td>
                    </tr>

                    </tbody>
                </table>
            </form>
        </div>
    );
}

10.11 Output

Here’s the output of all we did in section 10.
level-1-update-record

11.0 Delete Record with React

11.1 Change MainApp Render Method

On our MainApp component > render method > switch statement, add the ‘delete’ case. The render method should look like the following.

render: function() {

    var modeComponent =
        <ReadProductsComponent
        changeAppMode={this.changeAppMode} />;

    switch(this.state.currentMode){
        case 'read':
            break;
        case 'readOne':
            modeComponent = <ReadOneProductComponent productId={this.state.productId} changeAppMode={this.changeAppMode}/>;
            break;
        case 'create':
            modeComponent = <CreateProductComponent changeAppMode={this.changeAppMode}/>;
            break;
        case 'update':
            modeComponent = <UpdateProductComponent productId={this.state.productId} changeAppMode={this.changeAppMode}/>;
            break;
        case 'delete':
            modeComponent = <DeleteProductComponent productId={this.state.productId} changeAppMode={this.changeAppMode}/>;
            break;
        default:
            break;
    }

    return modeComponent;
}

11.2 Add DeleteProductComponent

This component contains the logic to delete a record. To explain each methods:

  • componentDidMount – on mount, change header text
  • onDelete – handle single row deletion, invoked when user clicked the “Yes” button
  • render – will show the question “Are you sure?” with “Yes” and “No” buttons.
var DeleteProductComponent = React.createClass({

    componentDidMount: function() {
        $('.page-header h1').text('Delete product');
    },

    onDelete: function(e){
        var productId = this.props.productId;

        $.post("api/delete_products.php",
            { del_ids: [productId] },
            function(res){
                this.props.changeAppMode('read');
            }.bind(this)
        );
    },

    render: function() {

        return (
            <div className='row'>
                <div className='col-md-3'></div>
                <div className='col-md-6'>
                    <div className='panel panel-default'>
                    	<div className='panel-body text-align-center'>Are you sure?</div>
                    	<div className='panel-footer clearfix'>
                    		<div className='text-align-center'>
                                <button onClick={this.onDelete}
                                    className='btn btn-danger m-r-1em'>Yes</button>
                                <button onClick={() => this.props.changeAppMode('read')}
                                    className='btn btn-primary'>No</button>
                    		</div>
                    	</div>
                    </div>
                </div>
                <div className='col-md-3'></div>
            </div>
        );
    }
});

11.3 Create delete_products.php

The previous section will not work without delete_product.php file. Create the file inside the “api” folder. The file should have the following code.

<?php
// if the form was submitted
if($_POST){

	// include core configuration
	include_once '../config/core.php';

	// include database connection
	include_once '../config/database.php';

	// product object
	include_once '../objects/product.php';

	// class instance
	$database = new Database();
	$db = $database->getConnection();
	$product = new Product($db);

	$ins="";
	foreach($_POST['del_ids'] as $id){
		$ins.="{$id},";
	}

	$ins=trim($ins, ",");

	// delete the product
	echo $product->delete($ins) ? "true" : "false";
}
?>

11.4 Object Method To Delete A Product

The previous section will not work without the following method on our Product object class.

// delete selected products
public function delete($ins){

    // query to delete multiple records
    $query = "DELETE FROM products WHERE id IN (:ins)";

    $stmt = $this->conn->prepare($query);

	// sanitize
	$ins=htmlspecialchars(strip_tags($ins));

	// bind the parameter
	$stmt->bindParam(':ins', $ins);

    if($stmt->execute()){
        return true;
    }else{
        return false;
    }
}

11.5 Output

level-1-delete-record

12.0 Download Source Codes

You can get the source code by following the whole, well detailed tutorial above. But isn’t it more convenient if you can just download the complete source code we used, and play around it?

There’s a small fee in getting the complete source code, it is small compared to the:

✔ Value or skill upgrade it can bring you, orYES
✔ Income you can get from your website project or business.YES
✔ Precious time you save.YES

For a limited time, I will give you the source code for a low price. DOWNLOAD THE SOURCE CODE LEVEL you desire by clicking its the BUY button below.

12.1 Download the LEVEL 1 Source Code

FEATURESLEVEL 1
PDO extension usedYES
Bootstrap UI enabledYES
Facebook ReactYES
Create productYES
Read productYES
Update productYES
Delete productYES
Category selection for create and update product.YES
Price display with dollar signYES
SQL file in “dev” folderYES
LEVEL 1: BUY AND DOWNLOAD NOW USING
* You can use your debit or credit card with PayPal.

12.2 Download the LEVEL 2 Source Code

FEATURESLEVEL 2
All features of LEVEL 1 aboveYES
Search recordsYES
Show records when search box is emptyYES
Routing works for search, page, create, edit and delete pagesYES
Export CSVYES
Multiple deleteYES
PaginationYES
Pagination in searchYES
Go to page numberYES
Limit go to page number based on number of pagesYES
Buttons with glyphiconsYES
LEVEL 1: BUY AND DOWNLOAD NOW USING
* You can use your debit or credit card with PayPal.

Do you need more reasons to get it?

MORE REASONS TO DOWNLOAD THE CODEALL
Use new skills for your multiple projectsYES
Save huge amount of time learning PHP, MySQL and ReactYES
Code examples are direct to the pointYES
Well explained and commented source codeYES
Fast and friendly email supportYES
Free source code updatesYES

12.4 Download ALL LEVELS in Separate Packages

DOWNLOAD ALL 2 PACKAGES ABOVEALL
LEVEL 1 source code packageYES
LEVEL 2 source code packageYES
LEVEL 1: BUY AND DOWNLOAD NOW USING
* You can use your debit or credit card with PayPal.

If you have any more questions, please feel free to contact me now. You can do it by sending a message on our official Facebook page, or via my email mike@codeofaninja.com

Thanks for supporting our website and projects here at codeofaninja.com!

13.0 What’s Next?

Next, we will learn how to create, read, update and delete records with AngularJS, also known as Angular 1.x. Click the following link: AngularJS CRUD Tutorial

14.0 Related Source Codes

Here are the related source codes used in this tutorial.

15.0 Online Resources

Here are useful resources related to React.

16.0 Some Notes

#1 Found An Issue?

If you found a problem with this code, we can solve it faster via Email or FB message, please send me a message via email mike@codeofaninja.com, or via our official Facebook page!

Please be more detailed about your issue. Best if you can provide an error message and your test or page URL. Thanks!

Please feel free to comment if you have any questions, suggestions, found something wrong or want to contribute to this code.

#2 Become a true Ninja!

We constantly add new tutorials and improve our existing tutorials and source codes. Be one of the first to know an update by subscribing to our FREE newsletter. Get a FREE EBOOK as well. CLICK HERE TO SUBSCRIBE FOR FREE!

#3 Thank You!

Thanks for reading our step by step tutorial about PHP, MySQL and React CRUD Tutorial!