Creating Comment Form Using React, Redux, Webpack, Babel

In this article, we will create a simple comment form that displays a list of comments and allows the user to enter a new comment using React, Redux, Webpack, Babel. We will be using below Tools/libraries to build the application.

In this article, we will create a simple comment form that displays a list of comments and allows the user to enter a new comment using React, Redux, Webpack, Babel. We will be using the below Tools/libraries to build the application.

  1. Visual Studio Code
  2. React JS library
  3. Redux library
  4. Node package manager
  5. Webpack
  6. Babel
  7. Bootstrap for styling
  8. Javascript, ES6

react

React

React is a JavaScript library for building user interfaces. It allows developers to create web applications that use data and can change over time without reloading the page.

ES6 (ECMAScript 6)

JavaScript is a superset of ECMAScript. ECMAScript (ES) is a specification standardized by ECMAScript International. The 5th edition of the specification is called ES5. ES6 contains many new features for JavaScript. ECMAScript is used by applications to enable client-side scripting.

A good understanding of ES6 features is important for JavaScript developers.

Babel

Babel "transpiles" ES6 to ES5 to ensure that our code runs in all recent browsers. It transpiles newer ECMAScript features down into a format that is supported by older ECMAScript browsers.

It is for developers who write plain JavaScript using newer language features.

Babel is configured using .babelrc file in the root of the project.

Webpack

Webpack is a module builder. Webpack performs transpilation, concatenation, minification, and combines CSS into JavaScript. Webpack uses npm and module system.

Let us start creating the application.

Create a directory for the project and name it "react-redux-sampleapp".

  • mkdir react-redux-sampleapp
  • cd react-redux-sampleapp

In the above project, Initialize the project using npm by running below command

  • npm init -y

This generates package.json file in the project. npm uses package.json file to manage dependencies.

We will be using Visual Studio code to work on this project.

In the command prompt, inside the root project folder(react-redux-sampleapp), run the below command. This opens the project in Visual Studio Code.

Code .

Now let us setup Webpack. Webpack is used to build the project.

In Visual Studio Code, Go to View Tab then Integrated Terminal window. Install webpack by running below command.

npm i webpack --save-dev

After running this command, "--save-dev" adds webpack to the devDependencies section in package.json file which indicates that it is a development dependency.

iI also installs webpack package locally under node_modules folder.

Let's configure development server which enables webpack to launch your application inside the browser. Webpack also takes care of refreshing the browser every time you update a file.

Run the below command to setup development server

npm i webpack-dev-server --save-dev

You have to add webpack command inside package.json file as below. This runs webpack located within our npm scripts. The build script below builds the assets of the project in one go. Start script starts the webpack development server and hosts our application.

  1. "scripts": {  
  2.     "start":"webpack-dev-server --open",  
  3.     "build""webpack"  
  4.   },  

Webpack needs configuration file to run. So create webpack.config.js file inside the project folder.

React components are generally written using Javascript ES6 which needs to be transformed to be understood by browsers. Babel loader is webpack loader which transforms ES6 code to the browser in understandable code. Babel-loader uses babel which needs to be configured to use below two presets.

  • babel-preset-env : Compiles JavaScript ES6 down to ES5.
  • babel-preset-react: Compiles JSX to Javascript.

Get the above dependencies using the below command. This adds the dependencies to the devDependencies section in package.json file.

npm i babel-loader babel-core babel-preset-env babel-preset-react --save-dev

babel-core is a compiler for writing next generation JavaScript which contains Node API.

Let's configure babel by adding .babelrc file at the root of the project. Add the below code to the .babelrc file. This tells babel to use these 2 presets.

  1. {  
  2.   "presets": ["env""react"]  
  3. }  

To display the React form, we have to tell Webpack to produce an HTML page. Webpacks needs two components for processing HTML: html-webpack-plugin and html-loader. Install these dependencies using below command

npm i html-webpack-plugin html-loader --save-dev

Let's set up webpack configuration in the webpack.config.json file as below.

  1. const path = require("path");  
  2. const HtmlWebpackPlugin = require("html-webpack-plugin");  
  3. module.exports = {  
  4.   entry: ["./src/js/app.js"], // Entry point for our application  
  5.   output: {  
  6.     path: path.resolve(__dirname, "dist"),// __dirname is the current directory. Our app will run from the dist folder.  
  7.     filename: "js/[name].js"  
  8.   },  
  9.   module: { // module section tells webpack what file types it should handle.  
  10.     rules: [ // this rule tells webpack to handle javascript files and use babel to transpile the code.  
  11.       {  
  12.         test: /\.js$/,  
  13.         exclude: /node_modules/,  
  14.         use: {  
  15.           loader: "babel-loader"  
  16.         }  
  17.       },  
  18.       {  
  19.         test: /\.html$/,  
  20.         use: [  
  21.             {  
  22.                 loader: "html-loader"  
  23.             }  
  24.         ]  
  25.     }  
  26.     ]  
  27.   },  
  28.   devServer: {  
  29.     contentBase: "./dist"  
  30.   },  
  31.   plugins:[  
  32.     new HtmlWebpackPlugin({  
  33.         template: "./src/index.html",  
  34.         filename:"./index.html"  
  35.     })  
  36. ]  
  37. };  

In the above code, path is used to resolve absolute path to project's root directory (where web pack.config.js should be located).

path is Node.js' native utility module.

require is Node.js' global function that allows you to extract contents from module.exports object inside some file.

The path.resolve() method resolves a sequence of paths or path segments into an absolute path to your file. It takes ./src/js/app.js as the entry point to produce an output into ./dist/js/main.js

Install react by running the below command from the View menu -> integrated Terminal

npm i react react-dom --save-dev

Install the prop-types package by running the below command

npm i prop-types --save-dev

Create the below directory structure to create React/Redux files for holding the code.

Create src folder inside root project folder. Inside the src folder, create js folder. Inside js folder, create actions, components, constants, reducers, store folders.

Inside the components folder, create container and presentation folders.

Inside the src folder, create app.js file and index.html file.

Inside the constants folder, create action-types.js file with the below content.

  1. export const ADD_COMMENT = "ADD_COMMENT";  

This file contains the constants defining the action-types we will be using in our actions.

Inside the actions folder create index.js file with the below content,

  1. import { ADD_COMMENT } from "../constants/action-types"  
  2. export const addComment = comment =>  
  3.  (  
  4.      {   
  5.          type: ADD_COMMENT,   
  6.          payload: comment   
  7.      }  
  8. );  

Here, addComment is an action object containing action type and payload (data).

Inside the reducers folder create index.js file with the below content,

  1. import { ADD_COMMENT } from "../constants/action-types";  
  2. const initialState = {  
  3.   comments: []  
  4. };  
  5. const rootReducer = (state = initialState, action) => {  
  6.   switch (action.type) {  
  7.     case ADD_COMMENT:  
  8.       return { ...state,comments:[...state.comments,action.payload]};  
  9.     default:  
  10.       return state;  
  11.   }  
  12. };  
  13. export default rootReducer;  

Reducer performs the action based on the action type and returns the state. Reducer is a pure function which does not mutate the state but returns a new copy of the state. It does not have side effects because it does not modify the state.

In this code, the initialstate, comments array does not change. The state returned is a copy of the initial state containing the old comments and the newly added comment from the payload. The spread operator (...) used here does not mutate the state rather it creates a copy of the state.

To support spread operator in Babel, Install Object rest spread transform using below command

npm i --save-dev babel-plugin-transform-object-rest-spread

Open up .babelrc and update the configuration,

  1. {  
  2.     "presets": ["env""react"],  
  3.     "plugins": ["transform-object-rest-spread"]  
  4.   }  

Create index.js file inside the store folder with the following content,

  1. import { createStore } from "redux";  
  2. import rootReducer from '../reducers/index';  
  3. const store = createStore(rootReducer);  
  4. export default store;  

Here we are using createStore to create the redux store which holds the state tree of your application.

Install uuid using below command. Uuid is used to generate a new id every time a new comment is added.

npm i uuid --save-dev

In order to connect redux with react we have to install the react-redux and redux library using below command

npm i react-redux --save-dev

Install Redux by runnning the below command

npm i redux --save-dev

Inside the container folder, create a new file named Form.js which contains our React component with the following data.

  1. import React, { Component } from 'react';  
  2. import { connect } from 'react-redux';  
  3. import uuidv1 from "uuid";  
  4. import { addComment } from "../../actions/index"  
  5.   
  6. const mapDispatchToProps = ( dispatch ) => {  
  7.     return {  
  8.         addComment: comment => dispatch (addComment(comment))  
  9.     };  
  10. };  
  11.   
  12. class ConnectedForm extends Component{  
  13.     constructor(){  
  14.         super();  
  15.         this.state = {  
  16.             title:""  
  17.         };  
  18.         this.handleChange = this.handleChange.bind(this);  
  19.         this.handleSubmit = this.handleSubmit.bind(this);  
  20.     }  
  21.      
  22.     handleSubmit(event) {  
  23.         event.preventDefault();  
  24.         const { title } = this.state;  
  25.         const id = uuidv1();  
  26.         this.props.addComment({ title,id });  
  27.         this.setState({ title: ""});  
  28.     }  
  29.   
  30.     handleChange(event){  
  31.         this.setState({  
  32.             [event.target.id]: event.target.value  
  33.         })  
  34.     }  
  35.     render(){  
  36.         const { title } = this.state;  
  37.      return (  
  38.      <form onSubmit={this.handleSubmit}>  
  39.     <div  className="form-group mt-3">  
  40.     <label htmlFor="title"><strong>Enter your comments below</strong></label>  
  41.     <textarea type="text" className="form-control"  id="title"  
  42.     value={title} onChange={this.handleChange} />  
  43.      <button type="submit" className="btn btn-success btn-lg mt-2">  
  44.     SAVE  
  45.     </button>  
  46.     </div>  
  47.      
  48.     </form>  
  49.         );  
  50.     }  
  51. }  
  52.   
  53. const Form = connect(null,mapDispatchToProps)(ConnectedForm);  
  54. export default Form;  

This component creates a form with textarea for the user to comment. When the user enters text in the textarea, onchange event is fired and it calls the handleChange method. This method saves the state in the local state object.

When the user clicks the save button, onSubmit event of the form is raised which calls the handleSubmit method. This method assigns the title from the local state to title const, and creates a new id using uuidv1 function. It creates a new comment object with these two values of title and id and passes it to the addComment callback prop.

mapDispatchToProps here is an object which injects the addComment prop into our component. mapDispatchToProps accepts dispatch function as an argument and returns addComment prop to inject into our component. addComment prop dispatches addComment action passing the comment object.

The addComment action creator uses the comment object to generate an action object in the actions/index.js file,that will be dispatched. This dispatches an action that internally calls the reducer based on the action type defined in the action to add the comment to the comment list.

Provider makes the Redux store available to the entire React application. React must talk to the Redux store for accessing the state and dispatching actions.

We are using connect method which connects the React component with the redux store. You will use connect with the below two arguments in this application depending on the context.

  • the mapStateToProps function
  • the mapDispatchToProps function

 

mapStateToProps function connects a part of the Redux state to the props of a React component. React component will have access to the exact part of the store it needs.

mapDispatchToProps connects Redux actions to React props. This way a connected React component will be able to dispatch actions through reducers.

Add List.js file inside the container folder with the following content.

  1. import React from "react";  
  2. import { connect } from "react-redux";  
  3.   
  4. const mapStateToProps = state => {  
  5.     return { comments: state.comments };  
  6. };  
  7.   
  8. const commentList = ( { comments }) => (  
  9.     <ul className="list-group list-group-flush">  
  10.     {  
  11.         comments.map( el => (  
  12.             <li className="list-group-item" key={el.id}>  
  13.             { el.title }  
  14.             </li>  
  15.         ))  
  16.     }  
  17.     </ul>  
  18. );  
  19.   
  20. const List = connect(mapStateToProps)(commentList);  
  21. export default List;  

The List component here displays the list of comments. The comments are stored in the redux store.

To retrieve the comments from the redux store we are using connect method of react-redux library

mapStateToProps function connects the list of comments from the Redux state to the comments prop of our commentList component.

Now open app.js file and add the following code,

  1. import React from "react";  
  2. import { render } from "react-dom";  
  3. import { Provider } from "react-redux";  
  4. import store from "../js/store/index";  
  5. import App from "../js/components/presentation/App";  
  6. render(  
  7.   <Provider store={store}>  
  8.     <App />  
  9.   </Provider>,  
  10.   document.getElementById("create-comment-form")  
  11. );  

In this file, Provider is responsible for providing redux store to our react application. Thus it wraps our app component.

In the ./src/js/components/presentation folder, add App.js file and add the below code,

  1. import React from 'react';  
  2. import List from '../container/List';  
  3. import Form from '../container/Form';  
  4. const App = () => (  
  5.         <div className="row-mt-4">  
  6.         <div className="col-md-4 offset-md-1">  
  7.         <h1> Comments </h1>  
  8.         <List/>  
  9.         <Form />  
  10.         </div>  
  11.         </div>  
  12. );  
  13.   
  14. export default App;  

This is a presentation component that does not contain any reference to state. It exposes App component comprised of List and Form components displaying the list of comments and Add comment form respectively.

Open Index.html file and add the below code,

  1. <!DOCTYPE html>  
  2. <html lang="en">  
  3. <head>  
  4.     <meta charset="utf-8">  
  5.     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" >  
  6.     <title>React Redux sample app</title>  
  7. </head>  
  8. <body>  
  9.     <div class="container">  
  10.         <div class="row mt-5">  
  11.             <div class="col-md-10 offset-md-1">  
  12.                 <!-- <p>Add Comment</p> -->  
  13.                 <div id="create-comment-form">  
  14.                     <!-- form -->  
  15.                 </div>  
  16.             </div>  
  17.         </div>  
  18.     </div>  
  19. </body>  
  20. </html>  

This is the actual html page which will be displayed. It contains div with the id ‘create-comment-form’. Our react application will be rendered in this div.

Now run below command from the integrated terminal window ( Ctrl + ` ).

npm start

This hosts your application in the development server and opens the application on http://localhost:8080.

Browser should display the Comment Form with comment textbox and save button. Once you enter a comment and click on save, you should see the comment listed above the comments textbox.

The page will be displayed as shown below

react

After you enter few comments and save, the output should display the list of comments followed by the  comment form shown below.

react