Which is better to learn and work on, React.js or AngularJS?

AngularJS is an open-source front-end JS framework which is used for creating web and mobile apps whereas ReactJS is a JavaScript library that supports front end and back end adequacy for creating UI and web apps.

Angular 7 CRUD with Nodejs and MySQL Example

Angular 7 CRUD with Nodejs and MySQL Example

Angular7 CRUD with nodejs and mysql example - Hey there, Today we will proceed to create a demo for CRUD with Mysql, Express, Angular7(MEAN) and Nodejs from scratch using Angular CLI

Below are the requirements for creating the CRUD on MEAN

  • Node.js
  • Angular CLI
  • Angular 7
  • Mysql
  • IDE or Text Editor

We assume that you have already available the above tools/frameworks and you are familiar with all the above that what individually actually does.

So now we will proceed step by step to achieve the task.

1. Update Angular CLI and Create Angular 7 Application

At first, We have to update the Angular CLI to the latest version. Open the terminal then go to the project folder and then type the below command to update the Angular CLI

sudo npm install -g @angular/cli

Once the above task finishes, Next task is to create new angular application with below command. So go to your project folder and then type below command:

ng new angular7-crud

then go to the newly created folder of angular application with cd /angular7-crud  and type **ng serve. **Now, open the browser then go to http://localhost:4200 you should see this page.

Angular 7 CRUD with Nodejs and MySQL Example

2. Create a server with node.js express and Mysql for REST APIs

create a separate folder named server for server-side stuff, Then move inside folder and create server.js by typing touch server.js

Let’s have a look on the server.js file

let app = require('express')(),
server = require('http').Server(app),
bodyParser = require('body-parser')
express = require('express'),
cors = require('cors'),
http = require('http'),
path = require('path');
 
let articleRoute = require('./Routes/article'),
util = require('./Utilities/util');
 
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false }));
 
app.use(cors());
 
app.use(function(err, req, res, next) {
return res.send({ "statusCode": util.statusCode.ONE, "statusMessage": util.statusMessage.SOMETHING_WENT_WRONG });
});
 
app.use('/article', articleRoute);
 
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next();
});
 
/*first API to check if server is running*/
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../server/client/dist/index.html'));
})
 
 
server.listen(3000,function(){
console.log('app listening on port: 3000');
});

In the above file we can see, at the top, there are required packages for the app. Below that body parsing, middleware and routing is done.

The next task is to create routes and create a file article.js . So creating a folder name ‘Routes’ and adding article.js within it.

Add the below code for routing in article.js inside routing folder

let express = require('express'),
router = express.Router(),
util = require('../Utilities/util'),
articleService = require('../Services/article');
 
/**Api to create article */
router.post('/create-article', (req, res) => {
articleService.createArticle(req.body, (data) => {
res.send(data);
});
});
 
// /**Api to update article */
router.put('/update-article', (req, res) => {
articleService.updateArticle(req.body, (data) => {
res.send(data);
});
});
 
// /**Api to delete the article */
router.delete('/delete-article', (req, res) => {
articleService.deleteArticle(req.query, (data) => {
res.send(data);
});
});
 
/**Api to get the list of article */
router.get('/get-article', (req, res) => {
documentService.getArticle(req.query, (data) => {
res.send(data);
});
});
 
// /**API to get the article by id... */
router.get('/get-article-by-id', (req, res) => {
articleService.getArticleById(req.query, (data) => {
res.send(data);
});
});
 
module.exports = router;

Now create a folder named Utilities for all config, common methods and mysql connection config.

Now I am adding config values in a file named config.js

let environment = "dev";
 
let serverURLs = {
"dev": {
"NODE_SERVER": "http://localhost",
"NODE_SERVER_PORT": "3000",
"MYSQL_HOST": 'localhost',
"MYSQL_USER": 'root',
"MYSQL_PASSWORD": 'password',
'MYSQL_DATABASE': 'demo_angular7_crud',
}
}
 
let config = {
"DB_URL_MYSQL": {
"host": `${serverURLs[environment].MYSQL_HOST}`,
"user": `${serverURLs[environment].MYSQL_USER}`,
"password": `${serverURLs[environment].MYSQL_PASSWORD}`,
"database": `${serverURLs[environment].MYSQL_DATABASE}`
},
"NODE_SERVER_PORT": {
"port": `${serverURLs[environment].NODE_SERVER_PORT}`
},
"NODE_SERVER_URL": {
"url": `${serverURLs[environment].NODE_SERVER}`
}
};
 
module.exports = {
config: config
};

Now configure mysql connection. So I am writing the connection with database in a separate file. So creating a file named mysqkConfig.js under Utilities folder and adding the below line of code for mysql connection:

var config = require("../Utilities/config").config;
var mysql = require('mysql');
var connection = mysql.createConnection({
host: config.DB_URL_MYSQL.host,
user: config.DB_URL_MYSQL.user,
password: config.DB_URL_MYSQL.password,
database: config.DB_URL_MYSQL.database,
});
 
connection.connect(() => {
require('../Models/Article').initialize();
});
 
let getDB = () => {
return connection;
}
 
module.exports = {
getDB: getDB
}

Now I am creating separate file name util.js to save common methods and common status code/message:

// Define Error Codes
let statusCode = {
OK: 200,
FOUR_ZERO_FOUR: 404,
FOUR_ZERO_THREE: 403,
FOUR_ZERO_ONE: 401,
FIVE_ZERO_ZERO: 500
};
 
// Define Error Messages
let statusMessage = {
SERVER_BUSY : 'Our Servers are busy. Please try again later.',
DATA_UPDATED: 'Data updated successfully.',
DELETE_DATA : 'Delete data successfully',
 
};
 
module.exports = {
statusCode: statusCode,
statusMessage: statusMessage
}

Now the next part is model, So create a folder named Models and create a file **Article.js **and add the below code in it:

let mysqlConfig = require("../Utilities/mysqlConfig");
 
let initialize = () => {
mysqlConfig.getDB().query("create table IF NOT EXISTS article (id INT auto_increment primary key, category VARCHAR(30), title VARCHAR(24))");
 
}
 
module.exports = {
initialize: initialize
}

Now create DAO folder and add a file articleDAO.js for writting the mysql queries common functions:

let dbConfig = require("../Utilities/mysqlConfig");


 
let getArticle = (criteria, callback) => {
//criteria.aricle_id ? conditions += ` and aricle_id = '${criteria.aricle_id}'` : true;
dbConfig.getDB().query(`select * from article where 1`,criteria, callback);
}
 
let getArticleDetail = (criteria, callback) => {
    let conditions = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
dbConfig.getDB().query(`select * from article where 1 ${conditions}`, callback);
}
 
let createArticle = (dataToSet, callback) => {
console.log("insert into article set ? ", dataToSet,'pankaj')
dbConfig.getDB().query("insert into article set ? ", dataToSet, callback);
}
 
let deleteArticle = (criteria, callback) => {
let conditions = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
console.log(`delete from article where 1 ${conditions}`);
dbConfig.getDB().query(`delete from article where 1 ${conditions}`, callback);
 
}
 
let updateArticle = (criteria,dataToSet,callback) => {
    let conditions = "";
let setData = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
dataToSet.category ? setData += `category = '${dataToSet.category}'` : true;
dataToSet.title ? setData += `, title = '${dataToSet.title}'` : true;
console.log(`UPDATE article SET ${setData} where 1 ${conditions}`);
dbConfig.getDB().query(`UPDATE article SET ${setData} where 1 ${conditions}`, callback);
}
module.exports = {
getArticle : getArticle,
createArticle : createArticle,
deleteArticle : deleteArticle,
updateArticle : updateArticle,
getArticleDetail : getArticleDetail
}

Now one create Services folder and add a file article.js for all the logic of API

let async = require('async'),
parseString = require('xml2js').parseString;
 
let util = require('../Utilities/util'),
articleDAO = require('../DAO/articleDAO');
//config = require("../Utilities/config").config;
 
 
/**API to create the atricle */
let createArticle = (data, callback) => {
async.auto({
article: (cb) => {
var dataToSet = {
"category":data.category?data.category:'',
"title":data.title,
}
console.log(dataToSet);
articleDAO.createArticle(dataToSet, (err, dbData) => {
if (err) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.SERVER_BUSY });
return;
}
 
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DATA_UPDATED,"result":dataToSet });
});
}
//]
}, (err, response) => {
callback(response.article);
});
}
 
/**API to update the article */
let updateArticle = (data,callback) => {
async.auto({
articleUpdate :(cb) =>{
if (!data.id) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.PARAMS_MISSING })
return;
}
console.log('phase 1');
var criteria = {
id : data.id,
}
var dataToSet={
"category": data.category,
"title":data.title,
}
console.log(criteria,'test',dataToSet);
                    articleDAO.updateArticle(criteria, dataToSet, (err, dbData)=>{
                        if(err){
cb(null,{"statusCode":util.statusCode.FOUR_ZERO_ONE,"statusMessage":util.statusMessage.SERVER_BUSY});
                        return; 
                        }
                        else{
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DATA_UPDATED,"result":dataToSet });                        
                        }
                    });
}
}, (err,response) => {
callback(response.articleUpdate);
});
}
 
/**API to delete the subject */
let deleteArticle = (data,callback) => {
console.log(data,'data to set')
async.auto({
removeArticle :(cb) =>{
if (!data.id) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.PARAMS_MISSING })
return;
}
var criteria = {
id : data.id,
}
articleDAO.deleteArticle(criteria,(err,dbData) => {
if (err) {
console.log(err);
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.SERVER_BUSY });
return;
}
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DELETE_DATA });
});
}
}, (err,response) => {
callback(response.removeArticle);
});
}
 
/***API to get the article list */
let getArticle = (data, callback) => {
async.auto({
article: (cb) => {
articleDAO.getArticle({},(err, data) => {
if (err) {
cb(null, {"errorCode": util.statusCode.INTERNAL_SERVER_ERROR,"statusMessage": util.statusMessage.SERVER_BUSY});
return;
}
cb(null, data);
return;
});
}
}, (err, response) => {
callback(response.article);
})
}
 
/***API to get the article detail by id */
let getArticleById = (data, callback) => {
async.auto({
article: (cb) => {
let criteria = {
"id":data.id
}
articleDAO.getArticleDetail(criteria,(err, data) => {
if (err) {
console.log(err,'error----');
cb(null, {"errorCode": util.statusCode.INTERNAL_SERVER_ERROR,"statusMessage": util.statusMessage.SERVER_BUSY});
return;
}
cb(null, data[0]);
return;
});
}
}, (err, response) => {
callback(response.article);
})
}
 
module.exports = {
createArticle : createArticle,
updateArticle : updateArticle,
deleteArticle : deleteArticle,
getArticle : getArticle,
getArticleById : getArticleById
};

3. Create angular component for performing CRUD task of article

ng g component article

Above command will generate all required files for build article component and also automatically added this component to app.module.ts.

create src/app/article/article.component.css (0 bytes)
create src/app/article/article.component.html (23 bytes)
create src/app/article/article.component.spec.ts (614 bytes)
create src/app/article/article.component.ts (321 bytes)
update src/app/app.module.ts (390 bytes)

Now we need to add HttpClientModule to app.module.ts. Open and edit src/app/app.module.ts then add this import. And add it to @NgModule imports after BrowserModule. Now our app.module.ts will have following code:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
 
import { AppComponent } from './app.component';
import { ArticleComponent } from './article.component';
import { ArticleService } from './article.service';
 
@NgModule({
imports: [
BrowserModule,
HttpModule,
ReactiveFormsModule
],
declarations: [
AppComponent,
ArticleComponent
],
providers: [
ArticleService
],
bootstrap: [
AppComponent
]
})
export class AppModule { }

Now create a service file where we will make all the request to the server for CRUD operation. Command for creating service is ng g service artcle , for now I have just created a file named it article.service.ts. Let's have a look in the code inside this file.

import { Injectable } from '@angular/core';
import { Http, Response, Headers, URLSearchParams, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
 
import { Article } from './article';
 
@Injectable()
export class ArticleService {
//URL for CRUD operations
    articleUrl = "http://localhost:3000/article";
    //Create constructor to get Http instance
    constructor(private http:Http) {
    }
    
    //Fetch all articles
getAllArticles(): Observable<Article[]> {
return this.http.get(this.articleUrl+"/get-article")
              .map(this.extractData)
         .catch(this.handleError);
 
}
    //Create article
createArticle(article: Article):Observable<number> {
     let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: cpHeaders });
return this.http.post(this.articleUrl+"/create-article", article, options)
.map(success => success.status)
.catch(this.handleError);
}
    //Fetch article by id
getArticleById(articleId: string): Observable<Article> {
        let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
        console.log(this.articleUrl +"/get-article-by-id?id="+ articleId);
        return this.http.get(this.articleUrl +"/get-article-by-id?id="+ articleId)
             .map(this.extractData)
             .catch(this.handleError);
}   
    //Update article
updateArticle(article: Article):Observable<number> {
     let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
return this.http.put(this.articleUrl +"/update-article", article, options)
.map(success => success.status)
.catch(this.handleError);
}
//Delete article    
deleteArticleById(articleId: string): Observable<number> {
        let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
        return this.http.delete(this.articleUrl +"/delete-article?id="+ articleId)
             .map(success => success.status)
             .catch(this.handleError);
}   
    private extractData(res: Response) {
        let body = res.json();
return body;
}
private handleError (error: Response | any) {
        console.error(error.message || error);
        return Observable.throw(error.status);
}
}

In the above file we have made all the http request for the CRUD operation. Observables of rxjs library has been used to handle the data fetching from http request.

Now let's move to the next file, article.component.ts. Here we have all the login part of the app. Let's have a look code inside this file:

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
 
import { ArticleService } from './article.service';
import { Article } from './article';
 
@Component({
selector: 'app-article',
templateUrl: './article.component.html',
styleUrls: ['./article.component.css']
})
export class ArticleComponent implements OnInit {
//Component properties
allArticles: Article[];
statusCode: number;
requestProcessing = false;
articleIdToUpdate = null;
processValidation = false;
//Create form
articleForm = new FormGroup({
title: new FormControl('', Validators.required),
category: new FormControl('', Validators.required)   
});
//Create constructor to get service instance
constructor(private articleService: ArticleService) {
}
//Create ngOnInit() and and load articles
ngOnInit(): void {
     this.getAllArticles();
}
//Fetch all articles
 
getAllArticles() {
        this.articleService.getAllArticles()
         .subscribe(
data => this.allArticles = data,
                errorCode => this.statusCode = errorCode);
                
}
//Handle create and update article
onArticleFormSubmit() {
     this.processValidation = true;
     if (this.articleForm.invalid) {
     return; //Validation failed, exit from method.
     }
     //Form is valid, now perform create or update
this.preProcessConfigurations();
     let article = this.articleForm.value;
     if (this.articleIdToUpdate === null) {
     //Generate article id then create article
this.articleService.getAllArticles()
     .subscribe(articles => {
            
         //Generate article id    
         let maxIndex = articles.length - 1;
         let articleWithMaxIndex = articles[maxIndex];
         let articleId = articleWithMaxIndex.id + 1;
         article.id = articleId;
         console.log(article,'this is form data---');
         //Create article
    this.articleService.createArticle(article)
             .subscribe(successCode => {
                    this.statusCode = successCode;
                    this.getAllArticles();  
                    this.backToCreateArticle();
                 },
                 errorCode => this.statusCode = errorCode
             );
         });        
     } else {
  //Handle update article
article.id = this.articleIdToUpdate;        
     this.articleService.updateArticle(article)
     .subscribe(successCode => {
         this.statusCode = successCode;
                 this.getAllArticles();  
                    this.backToCreateArticle();
             },
         errorCode => this.statusCode = errorCode);  
     }
}
//Load article by id to edit
loadArticleToEdit(articleId: string) {
this.preProcessConfigurations();
this.articleService.getArticleById(articleId)
     .subscribe(article => {
            console.log(article,'poiuytre');
         this.articleIdToUpdate = article.id;
                    this.articleForm.setValue({ title: article.title, category: article.category });
                    this.processValidation = true;
                    this.requestProcessing = false;
         },
         errorCode => this.statusCode = errorCode);
}
//Delete article
deleteArticle(articleId: string) {
this.preProcessConfigurations();
this.articleService.deleteArticleById(articleId)
     .subscribe(successCode => {
         //this.statusCode = successCode;
                    //Expecting success code 204 from server
                    this.statusCode = 204;
                 this.getAllArticles();  
                 this.backToCreateArticle();
             },
         errorCode => this.statusCode = errorCode);
}
//Perform preliminary processing configurations
preProcessConfigurations() {
this.statusCode = null;
     this.requestProcessing = true;
}
//Go back from update to create
backToCreateArticle() {
this.articleIdToUpdate = null;
this.articleForm.reset(); 
     this.processValidation = false;
}
}

Now we have to show the task over browser, So lets have a look inside article.component.html file.

<h1 class="text-center">Angular 7 CRUD Demo App</h1>
<h3 class="text-center" *ngIf="articleIdToUpdate; else create">
Update Article for Id: {{articleIdToUpdate}}
</h3>
<ng-template #create>
<h3 class="text-center"> Create New Article </h3>
</ng-template>
<div>
<form [formGroup]="articleForm" (ngSubmit)="onArticleFormSubmit()">
<table class="table-striped" style="margin:0 auto;">
<tr><td>Enter Title</td><td><input formControlName="title">
   <label *ngIf="articleForm.get('title').invalid && processValidation" [ngClass] = "'error'"> Title is required. </label>
 </td></tr>
<tr><td>Enter Category</td><td><input formControlName="category">
   <label *ngIf="articleForm.get('category').invalid && processValidation" [ngClass] = "'error'"> Category is required. </label>
  </td></tr>  
<tr><td colspan="2">
   <button class="btn btn-default" *ngIf="!articleIdToUpdate">CREATE</button>
    <button class="btn btn-default" *ngIf="articleIdToUpdate">UPDATE</button>
   <button (click)="backToCreateArticle()" *ngIf="articleIdToUpdate">Go Back</button>
  </td></tr>
</table>
</form>
<br/>
<div class="text-center" *ngIf="statusCode; else processing">
<div *ngIf="statusCode === 201" [ngClass] = "'success'">
   Article added successfully.
</div>
<div *ngIf="statusCode === 409" [ngClass] = "'success'">
Article already exists.
</div>   
<div *ngIf="statusCode === 200" [ngClass] = "'success'">
Article updated successfully.
</div>   
<div *ngIf="statusCode === 204" [ngClass] = "'success'">
Article deleted successfully.
</div>   
<div *ngIf="statusCode === 500" [ngClass] = "'error'">
Internal Server Error.
</div> 
</div>
<ng-template #processing>
  <img *ngIf="requestProcessing" src="assets/images/loading.gif">
</ng-template>
</div>
<h3 class="text-center">Article List</h3>
<table class="table-striped" style="margin:0 auto;" *ngIf="allArticles">
<tr><th> Id</th> <th>Title</th><th>Category</th><th></th><th></th></tr>
<tr *ngFor="let article of allArticles" >
<td>{{article.id}}</td> <td>{{article.title}}</td> <td>{{article.category}}</td>
  <td><button class="btn btn-default" type="button" (click)="loadArticleToEdit(article.id)">Edit</button> </td>
  <td><button class="btn btn-default" type="button" (click)="deleteArticle(article.id)">Delete</button></td>
</tr>
</table>

Now since I have created server and client two separate folder for nodejs and angular task. So will run both the apps with npm start over two tabs of terminal.

On the browser, over link http://localhost:4200. App will look like below

Angular CRUD with Nodejs and MySQL Example

That’s all for now. Thank you for reading and I hope this post will be very helpful for creating CRUD operations with angular7,node.js & mysql.

================================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

How to perform CRUD operations and consume RESTful API using React?

How to perform CRUD operations and consume RESTful API using React?

In this post, we go through a tutorial on how to create a React application that can perform CRUD functions and take in data from a RESTful API. Recently we have experienced rapid growth of mobile, Internet-based communication. We have...

In this post, we go through a tutorial on how to create a React application that can perform CRUD functions and take in data from a RESTful API.

Recently we have experienced rapid growth of mobile, Internet-based communication. We have smartphones, tablets, netbooks, and other connected devices that create a need to serve appropriate content for each specific front-end profile. Also, the Internet is becoming available for new regions and social groups, constantly increasing web traffic. On the other side, users' computers and browsers, and JavaScript itself are getting more and more powerful, providing more possibilities for processing data on the web via the client-side. In this situation, the best solution can often be to send data as the response, instead of sending page content. That is, we don't need to reload the whole page on each request, but send back the corresponding data and let the client (front-end stuff) process the data.

We can develop a backend application exposing a remote API (usually based on the REST protocol) and a front-end (usually JavaScript) application, which communicates with the API and renders all the data on the device.

If backend data is consumed by humans, we need to develop a user interface (UI) to provide the possibility for users to manage the data. Modern web applications should have responsive and friendly UIs, ensuring adequate user experience. Also, modern UIs can be arbitrarily complex, with multi-panel, nested layouts, paging, progress bars, etc. In this case, the component model can be the right solution. React.js is a light-weight JavaScript framework, which is oriented toward the creation of component-based web UIs. React doesn't provide any means for communicating with the backend, but we can use any communication library from inside React components.

Before starting development, we need to set up a React.js development environment.

1. React.js Development Environment Set Up

There are several ways to use React.js. The simplest way is just to include React libraries in the

Learn React.js - Full Course for Beginners

Learn React.js - Full Course for Beginners

Learn the complete React-Redux front end system: React.js, Redux, React Router, React Hooks and Auth0

Other courses have made partial updates to older techniques and code, but this course is built from the ground up to include the most latest code and techniques from 2019+. You are getting the complete integrated modern techniques and code for React and Redux that are used in development today.

What you'll learn:

  • The Most Modern Version of React and Redux
  • Modern Authentication and Routing Patterns
  • The Knowledge and Skills to Apply to Front End Jobs
  • How React Works Under the Hood
  • The Knowledge to know how different Front End Technologies work together (enough knowledge to impress other engineers)
  • Modern ES5, ES6, ES7 Javascript Syntax.

Build a React Responsive Navigation Menu with React Hooks and Sass

Build a React Responsive Navigation Menu with React Hooks and Sass

In this video tutorial, we will learn how to build a responsive navbar with React, Reach Router, and Sass.

What Is Responsive Web Design?

Responsive Web design is the approach that suggests that design and development should respond to the user’s behavior and environment based on screen size, platform and orientation.

The practice consists of a mix of flexible grids and layouts, images and an intelligent use of CSS media queries. As the user switches from their laptop to iPad, the website should automatically switch to accommodate for resolution, image size and scripting abilities. One may also have to consider the settings on their devices; if they have a VPN for iOS on their iPad, for example, the website should not block the user’s access to the page. In other words, the website should have the technology to automatically respond to the user’s preferences. This would eliminate the need for a different design and development phase for each new gadget on the market.

Download and Run

git clone https://github.com/syntacticsolutions/react-navigation-menu.git
cd react-navigation-menu

In the project directory, you can run:

npm start

Build a React Responsive Navigation Menu with React Hooks and Sass

Runs the app in the development mode.

Open http://localhost:3000 to view it in the browser.
The page will reload if you make edits.

You will also see any lint errors in the console.

npm test

Launches the test runner in the interactive watch mode.

See the section about running tests for more information.

npm run build

Builds the app for production to the build folder.

It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.

Your app is ready to be deployed!

See the section about deployment for more information.

npm run eject

Note: this is a one-way operation. Once you eject, you can’t go back!

If you aren’t satisfied with the build tool and configuration choices, you can eject at any time. This command will remove the single build dependency from your project.

Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.

You don’t have to ever use eject. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.

Learn More

You can learn more in the Create React App documentation.

To learn React, check out the React documentation.

Code Splitting

This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting

Analyzing the Bundle Size

This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size

Making a Progressive Web App

This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app

Advanced Configuration

This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration

Deployment

This section has moved here: https://facebook.github.io/create-react-app/docs/deployment

npm run build fails to minify

This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

React-Redux with TypeScript

React-Redux with TypeScript

TypeScript is a typed superset of JavaScript. It has become popular recently in applications due to the benefits it can bring. If you are new to TypeScript it is highly recommended to become familiar with it first before proceeding.

TypeScript is a typed superset of JavaScript. It has become popular recently in applications due to the benefits it can bring. If you are new to TypeScript it is highly recommended to become familiar with it first before proceeding.

TypeScript is a great language to choose if you are a JavaScript developer and interested in moving towards a statically typed language. Using TypeScript is such a logical move for developers that are comfortable with JavaScript but haven’t written in languages that are statically typed (like C, JVM’s, Go, etc).

As I began my journey to TypeScript, (I’m most comfortable with JS, but have written a little bit in Go and C), I found it to be pretty easy to pick up. My initial thought was: “It really isn’t that bad typing all the argument in my function and typing the return value; what’s the fuss all about?” It was nice and simple until we had a project where we needed to create a React/Redux app in TypeScript.

It’s super easy to find material for React + JS, but as you begin to search for React + TS and especially React + Redux + TS, the amount of online tutorials (including YouTube videos) begin to dwindle significantly. I found myself scouring Medium, Stack Overflow, etc. for anything I could find to help explain how to set up the project, how types are flowing between files (especially once Redux is involved), and how to build with Webpack. This article is a way for me to solidify my knowledge of React + Redux + TS, and to hopefully provide some guidance for anyone else who is interested in using this tech stack for the front end. TypeScript is becoming more popular, so I hope this is useful to others in the community.

Prerequisites: I assume you’re aware of how React, Redux, and Webpack work, and also most concepts in TypeScript (at least interfaces and generics).

What are we gonna build? Just to keep it simple, we’ll build the infamous to-do list application. Remember that the purpose is to understand how to set up the project and know how TypeScript integrates with React and Redux. The features that this application will support are:

  1. Add a new item to a list.
  2. Remove an item from the list.

The code for the project can be found here: https://github.com/sterlingdeng/react-redux-ts-boilerplate.

For my project, I didn’t use create-react-app --typescript to get the project started. I found that it was a valuable learning experience to get it started from scratch. I’ll go step by step through the import files and folders needed to get this project up and running. Before we start, let me show you what the final structure looks like.

TS-Redux-React-Boilerplate
├── build
├── node_modules
├── public
│   ├── bundle.js
│   └── index.html
├── src
│   ├── App.tsx
│   ├── index.tsx
│   ├── actions
│   │   ├── actions.ts
│   │   └── index.ts
│   ├── components
│   │   ├── index.ts
│   │   └── TodoItem.tsx
│   ├── containers
│   │   └── TodoContainer.tsx
│   ├── reducers
│   │   ├── index.ts
│   │   └── todoReducers.ts
│   ├── store
│   │   └── store.ts
│   └── types
│       └── types.d.ts
├── .gitignore
├── package-lock.json
├── package.json
├── tslint.json
├── tsconfig.json
├── webpack.config.js
└── README.md

First, let’s look at the package.json file and install these dependencies using npm i.

"dependencies": {
    "@types/node": "^12.0.0",
    "@types/react": "^16.8.15",
    "@types/react-dom": "^16.8.4",
    "@types/react-redux": "^7.0.8",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-redux": "^7.0.3",
    "redux": "^4.0.1",
    "ts-loader": "^5.4.5",
    "typesafe-actions": "^4.2.0",
    "typescript": "^3.4.5",
    "webpack": "^4.30.0",
    "webpack-cli": "^3.3.1"
  }

Let’s first look at the dependencies with the format @types/[npm module here]. If you aren’t familiar with what these modules are, look up https://github.com/DefinitelyTyped/DefinitelyTyped. Since most modules are written in JavaScript, they aren’t written with proper type definitions. Once you attempt to import a module without any type information into a project that is all typed, TypeScript will complain. To prevent this, the community of contributors at DefinitelyTyped create high-quality type definition of the most commonly used JavaScript modules so that those modules will integrate as seamlessly as possible with TS.

You are probably familiar with the next four. ts-loader is needed because Webpack needs a plugin to parse .ts and .tsx files. (This is similar to babel.)

typesafe-actions is a library I use with Redux + TypeScript. Without it, the files can get quite noisy in terms of declaring types for the Store, Reducer, and Actions. This library provides methods that infer type definition for redux code so that the files are a bit cleaner and focused.

webpack and webpack-cli are used to bundle the .ts and .tsx files into one.js file that can be sent to the front end.

Next, let’s look at thetsconfig.json file. The purpose of this file is to configure how you want the ts compiler to run.

{
  "compilerOptions": {
    "baseUrl": ".",
    "outDir": "build/dist",
    "module": "commonjs",
    "target": "es5",
    "sourceMap": true,
    "allowJs": true,
    "jsx": "react"
  }
}

baseUrl denotes the folder. outDir directs the compiler where it should put the compiled code. module tells the compiler which JavaScript module types to use. target tells the compiler which JS version to target. sourceMap tells the compiler to create a bundle.js.map along with bundle.js. Because bundling will turn multiple files into one large .js file, troubleshooting code will be difficult because you wouldn’t easily know which file and at which line the code failed (since everything is just one big file). The .map file will map the bundled file to the respective unbundled file.

tslint.json provides options on how strict or loose you want the ts linter to be. The various options you can set for the linter can be found online.

Action Creators

Normally when I start projects with Redux, I begin at the action creators. Let’s quickly review the features that we need to implement: Adding an item to a list and removing an item from the list. This means we’ll need two action creators, one for adding and another for removing.

import { action } from "typesafe-actions";

// use typescript enum rather than action constants
export enum actionTypes {
  ADD = "ADD",
  DELETE = "DELETE"
}

export const todoActions = {
  add: (item: string) => action(actionTypes.ADD, item),
  delete: (idx: number) => action(actionTypes.DELETE, idx)
};

In the actions.ts file, I’m using the enum feature in TS to create the action constants. Secondly, I’m using the action method provided from the typesafe-actions module. The first argument you pass into the method is a string which represents that action, and the second argument is the payload. The add method will add an item to the list of to-dos, and the deletemethod will remove an item, based on the provided index, from the list of to-dos.

In terms of type safety, what we want in our reducers file is the proper type of the payload, given a specific action. The feature in TypeScript that provides this support is called discriminated union and type guarding. Consider the example below:

// this is an example of discriminated unions
// this file isn't used in the project

interface ActionAdd {
  type: "ADD",
  payload: string
}

interface ActionDelete {
  type: "DELETE",
  payload: number
}

type Actions = ActionAdd | ActionDelete

function reducer(a: Actions) {
  switch(a.type) {
    case "ADD" : {
      // payload is a string
    }
    case "DELETE" : {
      // payload is a number
    }
  }
}

Given the shape of the two action objects, we can discriminate between them based on the type property. Using control flow analysis, like if-else or switch-case statements, it’s very logical that in line 16, the only type that the payload can be is a string. Since we only have two actions, the remaining payload in line 22 will be a number. If you are interested in learning more about discriminated unions and type guarding, I would recommend learning more about it here and here.

Reducer

After defining our action creators, let’s create the reducer for this project.

import * as MyTypes from "MyTypes";
import { actionTypes } from "../actions/";

interface ITodoModel {
  count: number;
  list: string[];
}

export const initialState: ITodoModel = {
  count: 2,
  list: ["Do the laundry", "Do the dishes"]
};

export const todoReducer = (state: ITodoModel = initialState, action: MyTypes.RootAction) => {
  switch (action.type) {
    case actionTypes.ADD: {
      return {
        ...state,
        count: state.count + 1,
        list: [...state.list, action.payload]
      };
    }
    case actionTypes.DELETE: {
      const oldList = [...state.list];
      oldList.splice(action.payload, 1);
      const newList = oldList;

      return {
        ...state,
        count: state.count - 1,
        list: newList
      };
    }
    default:
      return state;
  }
};

In lines four through seven, I’m defining the model (or schema) of our Todostore. It will keep track of how many to-do items we have, as well as the array of strings. Lines nine through 12 are the initial state when the application first starts. Within the todoReducer function, we want type safety within the casestatements. Based on the earlier gist, we accomplished that by discriminated unions and type guarding, done by typing the action parameter. We have to first define an interface for every action object, and then create a union of them all and assign that to a type. This can get tedious if we have a lot of action creators — luckily, typesafe-actions has methods to help create the proper typing of the action creators without having to actually write out all the interfaces.

declare module "MyTypes" {
  import { StateType, ActionType } from "typesafe-actions";
  // 1 for reducer, 1 for action creators
  export type ReducerState = StateType<typeof import("../reducers").default>;
  export type RootAction = ActionType<typeof import("../actions/actions")>;
}

Ignoring line four for now and focusing on line five, we use a method called ActionType from the module, and import the actions from actions.ts to create the discriminated union types, which are then assigned to a type called RootAction. In line one of the todoReducers.ts, we import MyTypes and in line 14, we type the action parameter with MyTypes.RootAction. This allows us to have IntelliSense and autocompletion within the reducers!

Now that we have the reducer set up, the ReducerState type from the types.d.ts file allows TypeScript to infer the shape of the state object in the reducer function. This will provide IntelliSense when we try to access the payload object within the Reducer. An example of what that looks like is in the picture below.

IntelliSense in the Reducer

Redux Store

Finally, let’s hook up the reducer to the redux store.

import { createStore } from "redux";
import rootReducer from "../reducers";

const store = createStore(rootReducer);

export default store;

Let’s recap what we have accomplished up until this point. We have created and typed our action creators using the action method from typesafe-actions. We have created our types.d.ts file which provide type information on our action creators and reducer state. The reducer has been created and the actions are typed by using MyTypes.RootAction, which provide invaluable auto-completion information of the payload within the reducer’s case statements. And lastly, we created our Redux store.

React and TS

Let’s change gears and begin working on creating and typing our React components. I’ll go over examples of how to properly type both function and class based components, along with instructions on how to type both the props and state (for stateful components).

App.tsx

import * as React from "react";
import TodoContainer from "./containers/TodoContainer";

export const App: React.FC<{}> = () => {
  return (
    <>
      <h1>React Redux Typescript</h1>
      <TodoContainer />
    </>
  );
};

App is a functional component that is typed by writing const App: React.FC<{}>. (FC refers to functional component.) If you aren’t familiar with generics (which is the <{}> ), I think of them like variables but for types. Since the shape of props and state can differ based on different use cases, generics are a way for us to, well, make the component generic! In this case, App doesn’t take any props; therefore, we pass in an empty object as the generic. How do we know that the generic is specifically for props? If you use VS code, IntelliSense will let you know what type it needs.

Where it says <P = {}> , it means type {} has been assigned to P, where Pstands for props. For class-based components, React will use S to refer to state. App is a functional component that receives no props and is not connected to the Redux store. Let’s go for something a little more complicated.

TodoContainer.tsx

import * as React from "react";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import * as MyTypes from "MyTypes";
import { actionTypes } from "../actions";
import { TodoItem } from "../components";

interface TodoContainerState {
  todoInput: string;
}

interface TodoContainerProps {
  count: number;
  todoList: string[];
  addToDo: (item: string) => object;
  deleteToDo: (idx: number) => object;
}

class TodoContainer extends React.Component<TodoContainerProps, TodoContainerState> {
  constructor(props) {
    super(props);
    this.state = {
      todoInput: ""
    };
  }

  handleTextChange = e => {
    this.setState({
      todoInput: e.target.value
    });
  };

  handleButtonClick = () => {
    this.props.addToDo(this.state.todoInput);
    this.setState({
      todoInput: ""
    });
  };

  handleDeleteButtonClick = (idx: number) => {
    console.log("deleting", idx);
    this.props.deleteToDo(idx);
  };

  render() {
    let todoJSX: JSX.Element[] | JSX.Element;
    if (!this.props.todoList.length) {
      todoJSX = <p>No to do</p>;
    } else {
      todoJSX = this.props.todoList.map((item, idx) => {
        return (
          <TodoItem item={item} key={idx} idx={idx} handleDelete={this.handleDeleteButtonClick} />
        );
      });
    }

    return (
      <div>
        {todoJSX}
        <input
          onChange={this.handleTextChange}
          placeholder={"New To Do Here"}
          value={this.state.todoInput}
        />
        <button onClick={this.handleButtonClick}>Add To Do</button>
      </div>
    );
  }
}

const MapStateToProps = (store: MyTypes.ReducerState) => {
  return {
    count: store.todo.count,
    todoList: store.todo.list
  };
};

const MapDispatchToProps = (dispatch: Dispatch<MyTypes.RootAction>) => ({
  addToDo: (item: string) => dispatch({ type: actionTypes.ADD, payload: item }),
  deleteToDo: (idx: number) => dispatch({ type: actionTypes.DELETE, payload: idx })
});

export default connect(
  MapStateToProps,
  MapDispatchToProps
)(TodoContainer);

OK, TodoContainer.tsx is the most complicated of them all, but I’ll walk you through what’s going on in the code. TodoContainer is a React Class Component because I need it to hold in its state the value for the input box. It is also connected to the redux store, so it’ll have MapStateToProps and MapDispatchToProps . First, I’ve definedTodoContainerState . Since I’ll be holding the value of the input box in state, I’ll type the property as a string.

Next, I’ve defined TodoContainerProps, which will be the shape of the Container’s props.

Because class-based components can have both state and props, we should expect that there should be at least two generics that we need to pass into React.Component.

P for Props and S for State

If you mouse over React.Component, you can see that it takes in three generics, P, S, and SS. The first two generics are props and state. I’m not quite sure whatSS is and what the use case is. If anyone knows, please let me know in the comments below.

After passing in the generics into React.Component , IntelliSense and autocompletion will work within this.state and for props.

Next, we want to type MapStateToProps and MapDispatchToProps. This is easily achievable by leveraging the MyTypes module that we built in the redux section. For MapStateToProps, we assign the store type to be MyTypes.ReducerState. An example of the IntelliSense it will provide is in the below screenshot.

IntelliSense for MapStateToProps

Lastly, we want to have type safety within MapDispatchToProps. The benefit that is provided is a type-safe payload given an action type.

Type-safe payloads

In the screenshot above, I purposely typed item as a boolean. Immediately, the TSServer will pick up that the boolean payload within MapDispatchToProps is not correct because it’s expecting the payload to be a string, given that the type is actionTypes.ADD. TodoContainer.tsx has the most going on since it is a class based React component, with both state and props, and is also connected to the store.

Before we wrap up, let’s look at our last component: TodoItem.tsx

TodoItem.tsx

This component is a functional component with props — code below.

import * as React from "react";

interface TodoItemProps {
  item: string;
  idx: number;
  handleDelete: (idx: number) => void;
}

export const TodoItem: React.FC<TodoItemProps> = props => {
  return (
    <span>
      {props.item}
      <button onClick={() => props.handleDelete(props.idx)}>X</button>
    </span>
  );
};

The shape of the props are defined in the interface TodoItemProps. The type information is passed into as a generic in React.FC. Doing so will provide auto-completion for props within the component. Awesome.

Another great feature that TypeScript provides when used with React is IntelliSense for props when rendering React Components within JSX. As an example, if you delete idx:number from TodoItemProps and then you navigate to TodoContainer.tsx, an error will appear at the place where you render <TodoItem />.

Property ‘idx’ does not exist

Because we removed idx from the TodoItemProps interface, TypeScript is letting us know that we have provided an additional prop that it couldn’t find, idx, into the component.

Lastly, let’s build the project using Webpack. In the command line, type npm run build. In the public folder within the root directory, you should see bundle.js alongsideindex.html. Open index.html in any browser and you should see a very simple, unstyled, to-do app.

After webpack build

I hope that I was able to demonstrate the power of TypeScript coupled with React and Redux. It may seem a bit overkill for our simple to-do list app — you just need to imagine the benefit of TS + React + Redux at scale. It will help new developers read the code quicker, provide more confidence in refactoring, and ultimately improve development speed.

If you need more reference and material, I used the following two Git repos to teach myself

Both these repos have proved invaluable for my learning, and I hope they will be the same for you.