React.js - Parent Child Component Communication and Event Handling

Posted by: Mahesh Sabnis , on 3/12/2019, in Category Reactjs
Views: 59332
Abstract: This React tutorials shows how to read the change event of all inputs using a single method and handle event of child component in the parent component.
React.js is an awesome JavaScript library for developing front-end for modern web applications. This library makes the development of UI painless. React provides Component-Based development that encapsulates component’s own state and it uses this state to build complex UI.

We can have multiple components in a React application. These components can define its state using state object. React components can communicate by passing state data to each other. The state data sent from the parent component to child component is accepted by the child component using props object.

 
In this React.js tutorial, we will go through the implementation of a React application. We will use the webpack configuration.

We need to use the webpack bundler and babel transpiler to transpile React application's code for loading and use bootstrap styles for the React application. This application is implemented using Visual Studio Code
 
Parent Child Component Communication
 

Parent Child Component Communication in React.js



Step 1: Create a new folder on the drive and open it in in VSCode. Name, the folder as react_reading_all_inputs.

Step 2: Open Node.js command prompt and navigate to the folder created in Step 1. Since we need package.json for defining application dependencies, run the following command from the command prompt:
npm init -y
This command will generate package.json. Modify the package.json file by defining required dependencies and devDependencies as shown in the following code:
{
  "name": "react-reading-all-inputs",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "start": "webpack-dev-server --hot"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/react": "^16.0.40",
    "babel-core": "^6.4.5",
    "babel-loader": "^6.2.1",
    "babel-preset-es2015": "^6.3.13",
    "babel-preset-react": "^6.3.13",
    "bootstrap": "4.0.0",
    "css-loader": "^0.27.3",
    "file-loader": "^0.10.1",
    "jquery": "3.3.1",
    "jshint": "^2.9.5",
    "prop-types": "^15.6.1",
    "react": "16.4.2",
    "react-bootstrap": "0.32.1",
    "react-dom": "16.4.2",
    "react-hot-loader": "^1.3.1",
    "style-loader": "^0.13.2",
    "url-loader": "^0.5.8",
    "webpack": "1.13.2",
    "webpack-dev-server": "1.16.2"
  },
  "dependencies": {
    "react": "^16.2.0",
    "react-dom": "^16.2.0",
    "webpack": "^1.13.2",
    "webpack-dev-server": "^1.16.2"
  }
}
Listing 1: Package.json

We need react and react-dom to use React.js Object model and DOM rendering.
 
Since WebPack is used for the application, we need webpack as the module bundler and webpack-dev-server to run the dev server so that the React application can be hosted, transpiled and executed with request processing. We also need all babel packages for transpilation of React code. The bootstrap and various loaders like css-loader, style-loader, etc. are used for styles which can be loaded from the server to the browser.

Run the following command to install all packaged mentioned in package.json:
npm install

Step 3: Add a new folder of name src in the project folder opened in VSCode. In this folder, add a new folder of name app. In the project folder, add a new file. Name this file as webpack.config.js and in this file add the following code:
var config = {
    entry: './main.js',
     
    output: {
       path:'./',
       filename: 'index.js',
    },
     
    devServer: {
       inline: true,
       port: 6001
    },
     
    module: {
       loaders: [
          {
             test: /\.jsx?$/,
             exclude: /node_modules/,
             loader: 'babel',
             query: {
                presets: ['es2015', 'react']
             }
          }, {
            test: /\.css?$/,
            loader: 'style!css?modules&localIdentName=[name]---[local]---[hash:base64:5]'
        },
        {
            test: /\.png$/,
            loader: "url-loader?limit=100000"
        },
        {
            test: /\.jpg$/,
            loader: "file-loader"
        },
        {
            test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
            loader: 'url?limit=10000&mimetype=application/font-woff'
        },
        {
            test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
            loader: 'url?limit=10000&mimetype=application/octet-stream'
        },
        {
            test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
            loader: 'file'
        },
        {
            test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
            loader: 'url?limit=10000&mimetype=image/svg+xml'
        }
       ]
    } 
 };
 
module.exports = config;
Listing 2: webpack.config.js 

Let us understand the different parts of the code in Listing 2.
 
  • The webpack.config.js file has an entry object. This object is used to specify the input file which will be used to define all components of the application.
  • The output object defines the output file generated based on all components in the application and various module loaders.
  • The devServer object defines the port on which the application is hosted.
  • The module object  defines various loaders e.g. babel, bootstrap, style, css, etc. These loaders will be used by the react application while execution.

Step 4: In the app folder, add a new file of name productcomponent.jsx. Add the following React-Component code in it:
import React, { Component } from "react";
class ProductComponent extends Component {
  constructor(props) {
    super(props);
    // 1. state declaring data members for the application
    this.state = {
      Categories: ["Electronics", "Electrical", "Civil", "Food"],
      ProductId: 0,
      ProductName: "",
      Price: 0,
      CategoryName: "",
      Products: [],
      Product: {
        ProductId: 0,
        ProductName: "",
        Price: 0,
        CategoryName: ""
      },
      TableColumnHeaders: []
    };
    this.generateTableColumnsHeaders();
    // self-binding for methods in constructor for Component
    this.handleInputChanges = this.handleInputChanges.bind(this);
  }
  // 2. method to load Product state object and push all its properties
  // in TableColumnHeaders array
  generateTableColumnsHeaders() {
    for (let p in this.state.Product) {
      this.state.TableColumnHeaders.push(p);
    }
  }
  // 3. method to handle changed event for all input elements
  handleInputChanges(e) {
    this.setState({ [e.target.name]: e.target.value });
  }
  // 4. method which is bind with the change event of select element
  // the call back os used for setState() method to read value of the selected option 
  handleCategoryNameChanged(e) {
    // the callback for "setState" will commit the
    // value selection from "select" element
    this.setState({ CategoryName: e.target.value }, function() {
      this.callBackValue(this.state.CategoryName);
    });
  }
  //5. clear values for all inputs
  clearInputs() {
    this.setState({ ProductId: 0 });
    this.setState({ ProductName: "" });
    this.setState({ Price: 0 });
    this.setState({ CategoryName: "" });
  }
  // 6. the save() method
  save() {
    // 6a. fact is setState() cannot set the values for Array
    // Define a temp array
    let tempArray = this.state.Products.slice();
    // 6b Push Data in array
    tempArray.push({
      ProductId: this.state.ProductId,
      ProductName: this.state.ProductName,
      Price: this.state.Price,
      CategoryName: this.state.CategoryName
    });
    // 6c. set the State on Products array based on tempArray
    this.setState({ Products: tempArray });
  }
 // 7. the callback function which may use the selected category name
  callBackValue(val) {}
  // 8. the method to print selected product from the table
  // this will set state values  
  print(data) {
    this.setState({ ProductId: data.ProductId });
    this.setState({ ProductName: data.ProductName });
    this.setState({ Price: data.Price });
    this.setState({ CategoryName: data.CategoryName });
  }
  // 9. render method
  render() {
    return (
<div className="container">
        <div className="form-group">
          <label htmlFor="ProductId">Product Id</label>
          <input
            type="text"
            className="form-control"
            value={this.state.ProductId}
            onChange={this.handleInputChanges}
            name="ProductId"
          />
        </div>
        <div className="form-group">
          <label htmlFor="ProductName">Product Name</label>
          <input
            type="text"
            className="form-control"
            value={this.state.ProductName}
            onChange={this.handleInputChanges}
            name="ProductName"
          />
        </div>
        <div className="form-group">
          <label htmlFor="Price">Product Price</label>
          <input
            type="text"
            className="form-control"
            value={this.state.Price}
            onChange={this.handleInputChanges}
            name="Price"
          />
        </div>
        <div className="form-group">
          <label htmlFor="CategoryName">Category Name</label>
          <select
            className="form-control"
            value={this.state.CategoryName}
            onChange={this.handleCategoryNameChanged}
            name="CategoryName"
          >
            {this.state.Categories.map((val, i) => (
                <Options key={i} data={val} />
            ))}
          </select>
        </div>
        <div className="form-group">
          <input
            type="button"
            value="New"
            onClick={this.clearInputs.bind(this)}
            className="btn btn-default"
          />
          <input
            type="button"
            value="Save"
            onClick={this.save.bind(this)}
            className="btn btn-success"
          />
        </div>
        <hr />
        <div>
          <table className="table table-bordered table-striped">
            <thead>
              <tr>
                {this.state.TableColumnHeaders.map((val, i) => (
                  <TableHeaders key={i} column={val} />
                ))}
              </tr>
            </thead>
            <tbody>
              {this.state.Products.map((val, i) => (
                <TableRow
                  key={i}
                  prd={val}
                  selectedRow={this.print.bind(this)}
                />
              ))}
            </tbody>
          </table>
        </div>
      </div>
  );
  }
}
// 10. the reusable Options component, that will be used for
// select
class Options extends Component {
  render() {
    return 
<option value={this.props.data}>{this.props.data}</option> }
}
// 11. the component to display table headers
class TableHeaders extends Component {
  render() {
    return 
<td>{this.props.column}</td>;}
}
// 12. component to display table rows
// this component also contains 'selectedData()' method
// this method calls the 'selectedRow()' method and passes selected product to it.
// the 'selectedData()' method will be executed
// when the click event is fired on the table-row   
class TableRow extends Component {
  selectedData() {
    this.props.selectedRow(this.props.prd);
  }
  render() {
    return (
<tr onClick={this.selectedData.bind(this)}>
        <td>{this.props.prd.ProductId}</td>
        <td>{this.props.prd.ProductName}</td>
        <td>{this.props.prd.Price}</td>
        <td>{this.props.prd.CategoryName}</td>
      </tr>
  );
  }
}

export default ProductComponent;
Listing 3: ProductComponent
 
In the React application, the JSX file contains component. The React Component is an autonomous player of the application. The component defines the state, events and the UI. The above code contains ProductComponent. This component has following specifications (Note: The following line numbers matches with comment numbers defined in Listing 3).

1. The state data members are declared. These are used to bind with UI elements. The constructor calls generateTableColumnsHeaders() method and perform binding for handleInputChanges() method. This steps creates a bound function.

2. The generateTableColumnsHeaders() method is defined. This method push Product properties in the TableColumnsHeaders array.

3. The handleInputChanges() method is used to handle change event for all input elements in the View. This reads value entered in each input element when change events is occurred. The important part of this method is that the name attribute of the input element must be same as the data members defined in a state object of the component.

4. The handleCategoryNameChanged() method will be used to set the CategoryName data member of the state object. Since this method is bound with the Html select element’s change event, it waits for the change event to complete so that is can read the value of the option of the select element. The handleCategoryNameChanged() method uses callback function to complete the setState to set value of the CategoryName based on the option value from the select element.

5. The clearInputs() method resets values for the data members declared in the state object.

6. The save() method will be used to read values for all state members and push it in the Products array. Since we cannot set the value of the array object using state members directly, the temporary array is declared and values of state members are pushed in this temporary array. This temporary array is used to set the Products array.

7. The callBackValue() method is called in the callback function for the setState() method of the handleCategoryNameChanged() method as explained in step 5.

8. The print() method will be used to set values of the state members based on the parameter passed to it.

9. The render() method contains Html UI. In this Html, all input elements are bind with the state members defined in the component. The onChange event is bind to the handleInputChanges() method. The select element is bind CategoryName state member. Options for the select element will be generated using Options component. The Html table is created using TableHeaders component for generating headers and table rows are generated using TableRow component.

10.  The Options component will generate Html option element. This component is used in the Html select element to generate options based on the Categories array.

11. The TableHeaders component is used to generate the table headers. This component is called in the Html table’s thead section. This generate Html td elements based on Product properties from the TableColumnHeaders array. This array is accepted by the TableHeaders component using props object.

12. The TableRow component generate Html table row based in Product properties. This component is called in the tbody section of Html table. Table rows are generated based on the Products array. The TableRow component contains selectData() method. This method calls selectedRow() method. The selectData() method is executed when the click event is fired on the table row. The selected Product object is passed to the selectedRow() method. This method is access in the TableRow component which is called in the tbody section of the Html table to generate table rows. The print() method explained in step 8 is bind with the selectedRow() method. The Product data passed to the selectedRow() method will be used by the print() method to set value for the state members defined in the ProductComponent.

Step 5: In the project folder, add a new Javascript file. Name this file as main.js. Add the following code in this file:
import React from 'react';
import ReactDom from 'react-dom';
import '!style!css!bootstrap/dist/css/bootstrap.min.css';
import ProductComponent from "./src/app/productcomponent.jsx";
ReactDom.render(,  document.getElementById('root')); 
Listing 4: main.js

Listing 4 shows an entry point to the application. This code imports react and react-dom packages.
 
The ReactDom class from react-dom package is used to render the React component. In our case, the ProductComponent will be rendered in Html element having id as root. The main.js file also imports bootstrap.min.css so that all style classes will be loaded in the browser that were used while rendering.
 
Step 6: Add a new html file in the project and name it as index.html. Add the following markup in it:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>React.js Application</title>
</head>
<body>
    <h1>The React Application</h1>
    <div id="root"></div>
    <script src="./index.js"></script>
</body>
</html>
Listing 5: index.html 

Listing 5 contains the root element and loads index.js. This fill will be generated by webpack. To run the application, open Node.js command prompt and navigate to the project folder. Run the following command:

npm run start

 
This command will build the package and server will start on port 6001 as defined in webpack.config.js. Open the chrome browser and enter http://localhost:6001 in the address-bar. This will load the component in the browser as shown in the following image:




Figure 1: Loading ProductComponent 

Enter data in the textboxes and click on Save button, this will add a new record in the table array as shown in the following image:


Figure 2: Adding Product record

To clear all textboxes, click on the New button. Add 2-3 records to generate rows in the table. When the table row is selected, its values will be displayed in textboxes.

Conclusion: We saw that a React.js component can have its own state and can contain child components. Data state is passed across parent-child components when child component accepts the state using the props object.

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions), and Front-end technologies like Angular and React. Follow him on twitter @maheshdotnet or connect with him on LinkedIn


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!