Check Application is Online ReactJS

It is a very common scenario to check whether the user is online while using your Web Application to make it robust. May be you need to just show a message to the user that the Internet connection is disrupted.

There is a very cool react package react-detect-offline available on npm that helps with this. It uses a default polling url to check whether you’re online. You can however change a few settings including the ping URL, enabled boolean and interval for ping duration. You can also change the timeout setting but I’ve not used it here in the polling object.

//say this code is inside constants.js file under common folder..
export const APIUrl = "https://testapi";
export const pingUrl = APIUrl + "/ping";
export const polling = {enabled: true, url: pingUrl, interval: 10000};
export const OfflineText = "You're Offline. Please check your Internet connection.";

In your component, import these objects and use it as below:

import { Offline, Online } from "react-detect-offline";
import { OfflineText, polling } from "../Common/Constants";

const styles = (theme) => ({
  OfflineText: {
    fontSize: "20px",
    color: "red",
  },
});

class MyComponent extends Component {
	render() {
		const { classes } = this.props;
		return (
		  <div id="loading">
            <Online polling={polling}>
              <img id="loading-image" src={loadingImg} alt="Loading..." />
            </Online>
            <Offline polling={polling}>
              <div className={classes.OfflineText}>{OfflineText}</div>
            </Offline>
		  </div>
		)
	}
}

The above example will ping every 10 seconds to check https://testapi for online status. This is called using http HEAD GET request. So please make sure both http verbs are allowed in your API.

For .net core users, the WebAPI should have the [HEAD] attribute set on the action and the method should be allowed in the Startup.cs file and Web.config.

Example of handlers in web.config as below:

<handlers>
            <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
            <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
            <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
            <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
        </handlers>

Prompt user with beforeunload on browser tab close React App

You might have a form created in your React App that user might try to close by closing the tab, reloading the page or close the browser directly. You might want to prompt the user in this case to alert them that there might be unsaved changes that might be lost.

The following example binds the “beforeunload” event to the App Component and the unbinds it on the componentwillunmount event. This could apply to form component and not the Parent App component.

export default class App extends React.Component {
  constructor(props) {
    super(props)
    this.handleUnload = this.handleUnload.bind(this);
  }

  componentDidMount() {
    window.addEventListener('beforeunload', this.handleUnload);
  }

  componentWillUnmount() {
    window.removeEventListener('beforeunload', this.handleUnload);
  }

  handleUnload(e) {
    var message = "\o/";

    (e || window.event).returnValue = message; //Gecko + IE
    return message;
  }

}

You can even call an API to make the necessary changes required for your App at the back-end before the browser unloads the Page. But please be careful in case the user decides to cancel and stays on the page. I’ve tested the above code on Google Chrome and Edge Chromium.

I haven’t really found a way that let’s you customize the prompt message in the modern browsers. And there is no way to capture the Leave/Cancel (stay) button click events. Let me know if you find a way to capture these events. However, I believe the browser has given control to the user more than the Page in case of unload.

Make React App compatible with IE11

IE11 is still among the most popular browsers in the world. Though Microsoft is promoting Edge Browser on Chromium engine. Many organizations have still not moved on to new offerings or taking their own time. Meanwhile, if you need to make your React Application compatible with IE11, check the steps below:

  1. Create the app, follow this post, if not already done.
  2. Add import 'react-app-polyfill/ie11'; as the first line in index.js file.
  3. For common standard functions like Array.find() that are not supported in IE 11 add import 'react-app-polyfill/stable'. This should be the 2nd line in index.js file.
  4. In package.json copy the production browserlist to development so you can test in IE11 and all other browsers that will work in production. Just add "ie 11" at the end of both production and development, browserlist section into package.json
  5. Delete the node_modules/.cache directory.
//index.js file:
import "react-app-polyfill/ie11";
import "react-app-polyfill/stable";
//browsers list in package.json file in reference to #4:
"browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all",
      "IE 11"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version",
      "IE 11"
    ]
  }

If you are also using arrow functions so you’ll also need babel to update that for you. The babel-plugin-transform-arrow-functions is your best solution for this. Please check the documentation here.

you might still face some issues in your functionality like additional spaces added by Material UI date control which fails in IE11 but not other browsers. This might need to be taken care of individually on operations using that date value.

Format Date in javascript to dd mmm yyyy

There is a simple way to write a function in Javascript to format the date as dd mmm yyyy. I’m using this method in my ReactJS code to simply format the date and set the state with the returned string wherever required.

formatDate(date) {
    if (date !== undefined && date !== "") {
      var myDate = new Date(date);
      var month = [
        "Jan",
        "Feb",
        "Mar",
        "Apr",
        "May",
        "Jun",
        "Jul",
        "Aug",
        "Sep",
        "Oct",
        "Nov",
        "Dec",
      ][myDate.getMonth()];
      var str = myDate.getDate() + " " + month + " " + myDate.getFullYear();
      return str;
    }
    return "";
  }

You can call this method from your JS code like this:

formatDate(new Date());

How to create Material UI Dialog with OK Cancel

Install the latest Material UI core package from npm:

npm install @material-ui/core @latest

Import the following components required for the Dialog box:

import React from "react";
import { withStyles } from "@material-ui/core/styles";
import Checkbox from "@material-ui/core/Checkbox";
import Dialog from "@material-ui/core/Dialog";
import DialogContent from "@material-ui/core/DialogContent";
import DialogActions from '@material-ui/core/DialogActions';
import { DialogContentText } from "@material-ui/core";
import MuiDialogTitle from "@material-ui/core/DialogTitle";
import CloseIcon from '@material-ui/icons/Close';
import Typography from '@material-ui/core/Typography';
import Button from "@material-ui/core/Button";
import IconButton from '@material-ui/core/IconButton';

Add the following styles for the Dialog content and buttons:

const styles = theme => ({
    root: {
      margin: 0,
      padding: theme.spacing(2),
    },
    closeButton: {
      position: 'absolute',
      right: theme.spacing(1),
      top: theme.spacing(1),
      color: theme.palette.grey[500],
    },
    dialogText: {
      color: theme.palette.blue
    }
  });

The following component will be used in the Dialog Title bar:

const DialogTitle = withStyles(styles)(props => {
    const { children, classes, onClose, ...other } = props;
    return (
      <MuiDialogTitle disableTypography className={classes.root} {...other}>
        <Typography variant="h6">{children}</Typography>
        {onClose ? (
          <IconButton aria-label="close" className={classes.closeButton} onClick={onClose}>
            <CloseIcon />
          </IconButton>
        ) : null}
      </MuiDialogTitle>
    );
  });

Add the class Component that will render the Dialog box:

class MyForm extends React.Component {
    constructor(props) {
      super(props);
	  this.handleChange = this.handleChange.bind(this);
	  this.handleDialogClose = this.handleDialogClose.bind(this);
	  this.handleDialogOK = this.handleDialogOK.bind(this);
	  this.state = {
		dialogText: '',
        isDialogOpen: false,
		isChecked: false
	  }
	}
	
	handleDialogOK() {
      console.log('Clicked OK!');
      this.setState({
        isDialogOpen: false
      });
    }
	
	handleDialogClose() {
      this.setState({
        isDialogOpen: false
      });
    }
	
	handleChange(e) {
      const target = e.target;
	  const value = target.checked;
	  
	  this.setState({
		isChecked: value,
		isDialogOpen: true
	  }, ()=> {console.log('Open Dialog')});
	}
	
	render() {
      const { classes } = this.props;
      return (
		<div>
			<Checkbox id="chkOpenDialog" onChange={this.handleChange} checked={this.state.isChecked}></Checkbox>
			<Dialog
                open={this.state.isDialogOpen}
                onClose={this.handleDialogClose}
                aria-labelledby="alert-dialog-title"
                aria-describedby="alert-dialog-description"
            >
              <DialogTitle id="customized-dialog-title" onClose={this.handleDialogClose}>
                  {"Message from Application"}
              </DialogTitle>
              <DialogContent>
                <DialogContentText id="alert-dialog-description" className={classes.dialogText}>
                  {this.state.dialogText}
                </DialogContentText>
                <DialogActions>
                  <Button color="primary" onClick={this.handleDialogOK}>
                      OK
                  </Button>
                  <Button color="primary" onClick={this.handleDialogClose}>
                      Cancel
                  </Button>
                </DialogActions>
              </DialogContent>  
          </Dialog>
		</div>
	  );
    }
}

export default withStyles(styles, { withTheme: true })(MyForm);

Setting the isDialogOpen to true on clicking checkbox will open the Dialog box with OK and Cancel Action buttons. Each Action button has it’s own handler.

Upload file in ReactJS to WebAPI

Create a new React App using create-react-app as explained in this Post. The sample ReactJS code is available here.

In the index.js file change the code as below:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

For the App.js file, change the code as below:

import React from 'react';       
import './App.css';
import Fileupload from './FileUpload'
function App() {    
  return (    
    <div className="App">    
      <Fileupload></Fileupload>
    </div>    
  );    
}
export default App;

Now comes the FileUpload component as FileUpload.js:

import React from 'react';
import axios from 'axios';

class Fileupload extends React.Component {

    constructor(props) {    
            super(props);    
            this.state = {    
                    file: '',    
        };    
    }

    async submit(e) {    
        e.preventDefault();    
        const url = `http://url`;    
        const formData = new FormData();    
        formData.append('body', this.state.file);    
        const config = {    
            headers: {    
                'content-type': 'multipart/form-data',
                'token': 'xxx'                
            },
        };
        const HTTP = axios.create({
            withCredentials: true
        });

        //return post(url, formData, config);
        return HTTP.post(url, formData, config);
    }
    setFile(e) {    
        this.setState({ file: e.target.files[0] });    
    }

    render() {    
        return (    
                <div className="container-fluid">    
                        <form onSubmit={e => this.submit(e)}>    
                                <div className="col-sm-12 btn btn-primary">    
                                        File Upload    
                        </div>    
                                <h1>File Upload</h1>    
                                <input type="file" onChange={e => this.setFile(e)} />    
                                <button className="btn btn-primary" type="submit">Upload</button>    
                        </form>    
                </div>    
        )    
    }
}
export default Fileupload

Here I’m using axios to call the API via Post request. Passing the credentials in the call with create.
The token part is optional in the Headers, you can add/remove headers based on your requirement.

From the WebAPI end, I’m using .Net core. You can receive the file in the Action method as below:

[HttpPost]
[Route("upload")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public IActionResult UploadForm([FromQuery] string username)
{
	var request = HttpContext.Request;
            
	if (request.Form.Files.Count > 0)
	{
		filename = request.Form.Files[0].FileName;
		extension = filename.Substring(filename.LastIndexOf(".")).ToLower();
		if (!string.IsNullOrEmpty(extension) && extension.Equals(".docx"))
		{
			using (var memoryStream = new MemoryStream())
			{
				request.Form.Files[0].CopyTo(memoryStream);
				
				// Upload the file if less than 2 MB
				if (memoryStream.Length < 2097152)
				{
					string check = Service.CheckifValid(username);
					if (check.Equals("ok"))
					{
						//Process the stream and pass the data to the back-end in the Service Layer.
						result = Service.UploadStream(memoryStream, filename, extension, username);
					}
					else
					{
						return Ok(check);
					}
				}
				else
				{
					return BadRequest("File size too large");
				}
			}
		}
		else
		{
			return BadRequest("File format not recognised.");
		}
	}
	//Other ToDos...
}

Download word file using axios get from WebAPI

The below code is using axios get method with Headers token to be passed with credentials which in this case would be Windows Authentication to the WebAPI.

The response returned by the Promise contains the header “content-disposition” which has the name of the file being downloaded.

The content-disposition header value is as below:

attachment; filename="SomeFile.docm"; filename*=UTF-8''SomeFile.docm

The content-type required for this file from the API is:

application/vnd.openxmlformats-officedocument.wordprocessingml.document

The returned response has the blob in the data property which then requires to be converted to a downloadble link. The filename is being extracted from the content-disposition header.

const HTTP1 = axios.create({
                  withCredentials: true
                });
const response = HTTP1.get('http://path/worddoc?userid=<someid>', {
		        headers: {
		          'token': 'xxxxx'
		        },
				responseType: 'blob'
		      }).then((response) => {
			
				const headerval = response.headers['content-disposition'];
				var filename = headerval.split(';')[1].split('=')[1].replace('"', '').replace('"', '');
				
				const downloadUrl = window.URL.createObjectURL(new Blob([response.data]));
				const link = document.createElement('a');
				link.href = downloadUrl;
				link.setAttribute('download', filename); //any other extension
				document.body.appendChild(link);
				link.click();
				link.remove();
			  });

For IE11 browser, downloading blob response type directly does not work. A workaround for this is to recognize the browser via User Agent modify the above code in Javscript for IE:

var ua = window.navigator.userAgent;
var msie = ua.indexOf(".NET ");

if(msie>0) {
          window.navigator.msSaveBlob(new Blob([response.data]), filename);
}

Use package react-svg-radar-chart to create Radar chart ReactJS

Create a basic ReactJS App using the create-react-app Post here.

Our final aim is to generate a Radar Chart using the npmjs package react-svg-radar-chart. Also, the dot markings on the chart should be able to show the current index and key, values on the chart on Hover as shown below.

The code is available in my Github profile.

Install the Package in your Project by running the following command in the Terminal:

npm install react-svg-radar-chart

Replace the code for the index.js file as shown below:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

Create the App.js file for the App component and add the below code:

import React, { Component } from "react";
import MyComp from "./MyComp";
import MyRadarComp from "./MyRadarComp";

export default class App extends Component {
    render() {
        return (
          <div className="App">
            <MyRadarComp />
          </div>
        );
      }
}

Create MyRadarComp.js file and add the below code:

import React from 'react';
 
import RadarChart from 'react-svg-radar-chart';
import 'react-svg-radar-chart/build/css/index.css'
 
export default class MyRadarComp extends React.Component {
  render() {
    const tooltipstyle = {
        position: 'relative',
        display: 'inline-block',
        borderBottom: '1px dotted black'
    }

    const tooltiptextstyle = {
        visibility: 'hidden',
        width: '220px',
        backgroundColor: 'black',
        color: '#fff',
        textAlign: 'center',
        borderRadius: '6px',
        padding: '5px 0',
    
        /* Position the tooltip */
        position: 'absolute',
        zIndex: '1',
    }

 	 const data = [
      {
        data: {
          battery: 0.5,
          design: .7,
          useful: 0.985,
          speed: 0.57,
          weight: 0.7
        },
        meta: { color: 'blue' }
      },
      {
        data: {
          battery: 0.6,
          design: .85,
          useful: 0.5,
          speed: 0.6,
          weight: 0.7
        },
        meta: { color: 'red' }
      },
      {
            data: {
              battery: 0.7,
              design: .8,
              useful: 0.9,
              speed: 0.67,
              weight: 0.8
            },
            meta: { color: '#58FCEC' }
        }
    ];
 
    const captions = {
      // columns
      battery: 'Battery Capacity',
      design: 'Design',
      useful: 'Usefulness',
      speed: 'Speed',
      weight: 'Weight'
    };

    const noSmoothing = points => {
        let d = 'M' + points[0][0].toFixed(4) + ',' + points[0][1].toFixed(4);
        for (let i = 1; i < points.length; i++) {
          d += 'L' + points[i][0].toFixed(4) + ',' + points[i][1].toFixed(4);
        }
        return d + 'z';
      };
       
      const defaultOptions = {
        size: 200,
        axes: true, // show axes?
        scales: 3, // show scale circles?
        captions: true, // show captions?
        captionMargin: 10,
        dots: true, // show dots?
        zoomDistance: 1.2, // where on the axes are the captions?
        setViewBox: (options) => `-${options.captionMargin} 0 ${options.size + options.captionMargin * 2} ${options.size}`, // custom viewBox ?
        smoothing: noSmoothing, // shape smoothing function
        axisProps: () => ({ className: 'axis' }),
        scaleProps: () => ({ className: 'scale', fill: 'none' }),
        shapeProps: () => ({ className: 'shape' }),
        captionProps: () => ({
          className: 'caption',
          textAnchor: 'middle',
          fontSize: 10,
          fontFamily: 'sans-serif'
        }),
        dotProps: () => ({
          className: 'dot',
          mouseEnter: (dot) => { 
              document.getElementById("tooltip").innerText = "index: " + dot.idx + ", key: " + dot.key + ", value: " + dot.value;
              document.getElementById("tooltip").style.visibility = "visible";
            },
          mouseLeave: (dot) => { 
              document.getElementById("tooltip").innerText = "";
              document.getElementById("tooltip").style.visibility = "hidden";
            }
        })
      };
 
    return (
      <div>
        <RadarChart
            captions={{
              // columns
              battery: 'Battery Capacity',
              design: 'Design',
              useful: 'Usefulness',
              speed: 'Speed',
              weight: 'Weight'
            }}
            data={data}
            size={400}
            options={defaultOptions}
          />
          <div id="divtool" style={tooltipstyle}><label id="tooltip" style={tooltiptextstyle}></label></div>
        </div>
    );
  }
}

Run the Project:

npm start

A few points about the MyRadarComp component:

  1. The code is taken from the npmjs package sample provided in the package description.
  2. I’ve modified the css a bit to show the dots on the chart and also show the values on the Hover event of the dots.
  3. The caption labels and the data property names should match in order to correctly display the data.

Setup Github Repo from Visual Studio Code

  1. Create the Project or directory on your file system. My local folder is called formik-sample. You can also create using the create-react-app Post here for a sample ReactJS App.
  2. Create a repository on Github. I’ve named it sample-form-reactjs.
  3. Click on Clone or Download. Copy the SSH link.
  4. Open the local folder in VS Code as created in Step 1.
  5. In the Terminal Window in VS Code, type the following commands:
  6. git config –global user.name someid
  7. git clone git@github.com:someid/sample-form-reactjs.git
  8. After this is done, just follow the same steps for git stage/commit/push etc.

For # 3:

However, if Step 5. gives error like below:

Check your network firewall settings if you’re doing it from Office. Try it out from your home and it should work.

Also, you can clone git@github which is SSH version but for that you have generate ssh keys and have to set it in local environment and if you don’t want to set up that then just use https version in the above clone command.

If you face branch related errors, since you might have already created a repo on Github, you can create a new branch as below:

git checkout -b some-sample

Then push the changes to the new branch:

git push --set-upstream origin some-sample

Using Fetch with React example async await

I’ve created a basic React App as described in my previous post. This is just a demo on how you can use fetch API to load data in the componentDidMount() lifecycle method with/without
async/await. I’ve not used any error handling yet.

The basic structure of the React App contains index.js and index.html files. The code is added to my Github profile.

Replace the code of the index.js file as below:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));

Create App.js file under the src directory and add the below code:

import React, { Component } from "react";
import MyComp from "./MyComp";
export default class App extends Component {
    render() {
        return (
          <div className="App">
            <MyComp />
          </div>
        );
      }
}

It’s time to create MyComp which is the sample component that will call the placeholder JSON API to show the list of users using Fetch GET request.

The code for MyComp is as shown below:

import React from "react";
export default class MyComp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            usernames : []
        };
    }
    componentDidMount() {
        fetch('https://jsonplaceholder.typicode.com/users')
            .then(res => res.json())
            .then(json => this.setState({ usernames: json }));
    }
    render() {
        return(
            <div>
                Hey guys!
                <ul>
                {this.state.usernames.map(user => (
                    <li key={user.username}>
                    Hello, {user.username}
                    </li>
                ))}
                </ul>
            </div>
        );
    }
}

Run the App using as below:

npm start

The above code will now use async/await. It is a clean asynchronous way to call the API by writing unblocking code just like promises and callbacks.

Run the following command in the terminal:

npm i @babel/preset-env @babel/plugin-transform-runtime @babel/runtime --save

Replace the componentDidMount() code as below:

async componentDidMount() {
        const response = await fetch(`https://jsonplaceholder.typicode.com/users`);
        const json = await response.json();
        this.setState({ usernames: json });
    }

The async function which in this case is the componentDidMount contains the await expression that pauses the execution of the async function. It waits until the passed Promise is resolved. It then resumes the async function’s execution and evaluates as the resolved value.

Run the App again to see the results which are the same in this case.

You can also include headers and credentials in the Fetch call as shown below:

const myHeaders = new Headers();
myHeaders.append('token', 'xxxx');
const response = fetch(`http://someurl/home/details?username=someuser`, {
	credentials: 'include',
	headers: myHeaders,
	cors: 'cors'
  });