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.

Advertisement

ReactJS Component life cycle

We can declare special methods on the component class to run some code when a component mounts and unmounts also called lifecycle methods.

A ReactJS component goes through the following phases: initialization, mounting, updating and unmounting.

constructor() – Not a lifecycle method exactly.

State should usually be assigned in the constructor as it is used to set the initial state of our component. You can directly set the state property inside the constructor as setState method is not available before the component
is mounted.

componentWillMount()

This was deprecated in post React 17.0. If you still continue using this, you should use UNSAFE_componentWillMount().

render()

Rendering happens when the component mounts or updates. It has to be pure i.e. it cannot modify the state using setState. It should only handle the rendering of your component to the UI.

componentDidMount()

You can make calls to a remote endpoint like an Web Service or API to load data and is the best place to do so. Using setSate here will cause state updates and cause another rendering that happens before the browser updates the UI.
States should usually be assigned in the constructor though as mentioned above.

componentDidUpdate()

This method is called in response to props or state changes. setState should only be used with caution as may lead to inifinite loop. You can wrap it in a condition as shown below:

componentDidUpdate(prevProps) {
 //Compare the props
 if (this.props.title !== prevProps.title) {
   //do something -- here we have compared the current props with the previous props before doing something.
 }
}

componentWillUnmount()

This is the end of the lifecycle before the component is unmounted and destroyed. This can be used for any cleanups like clearing storage caches.

Create a basic ReactJS App, to follow through the example. You can check this post to get started.

In the example below to check out how these Component methods are called to depict the life cycle, the output for all these methods are logged to the Console.

The constructor initializes the state of the Component. The render method is called after every ComponentWill methods. Once the Component is rendered to the DOM, the ComponentDid method is called.

The componentDidMount() method runs after the component output has been rendered to the DOM.

import React, { Component } from "react";

class TestComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      increment: this.props.increment
    };
    console.log(this.state.increment, "Initialized");
  }

  componentWillMount() {
    this.setState({
      increment: this.state.increment = parseInt(this.state.increment) + 1
    });
    console.log(this.state.increment, "Inside ComponentWillMount");
  }

  componentDidMount() {
    this.setState({
      increment: this.state.increment = parseInt(this.state.increment) + 1
    });
    console.log(this.state.increment, "Inside ComponentDidMount");
  }

  componentWillUnmount() {
    this.setState({
      increment: this.state.increment = parseInt(this.state.increment) + 1
    });
    console.log(this.state.increment, "Inside ComponentWillUnmount");
  }

  componentWillUpdate() {
    console.log(this.state.increment, "Inside ComponentWillUpdate");
  }

  componentDidUpdate() {
    console.log(this.state.increment, "Inside ComponentDidUpdate");
  }
  

  render() {
    console.log(this.state.increment, "Render method called");
    return (
      <React.Fragment>
        <div>{this.state.increment} Render method called!</div>
      </React.Fragment>
    );
  }
}

export default TestComponent;
Order of how the Methods were called
DOM output

Whenever the TestComponent component is removed from the DOM, React calls the componentWillUnmount() lifecycle method.

Internationalization with React JS i18n package

Building Single Page Applications (SPA) in React JS that require multi-language support require a way for the user to be able to select the preferred language and all or specific text on the page or site should change.

React supports a package i18next that helps load the default language and save the preferred language which the user selects from the UI. react-i18next is built on top of i18next and is a powerful internationalization framework for React JS. This post explains i18n usage for v10 or above.

This example is built on top of my other post for Client-side routing here.

You can find the i18n demo code in my Github profile here.

You need to install the package react-i18next using npm as below:

npm install i18next
npm install react-i18next

Initialize the i18next store placed under the config folder as below:

import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import en from "../locales/en";
import fr from "../locales/fr";
import it from "../locales/it";
import de from "../locales/de";
import es from "../locales/es";
i18n.use(initReactI18next).init({
  resources: {
    en,
    fr,
    it,
    de,
    es
  },
  fallbackLng: "en",
  debug: true,
  ns: ["translations"],
  defaultNS: "translations",
  keySeparator: false,
  interpolation: {
    escapeValue: false,
    formatSeparator: ","
  },
  react: {
    wait: true
  }
});
export default i18n;

The i18n package has a provider which would wrap your react App component.

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./components/App";
import * as serviceWorker from "./serviceWorker";
import { BrowserRouter as Router } from "react-router-dom";
import { I18nextProvider } from "react-i18next";
import i18n from "./config/i18n";
import "bootstrap/dist/css/bootstrap.min.css";
ReactDOM.render(
  <I18nextProvider i18n={i18n}>
    <Router>
      <App />
    </Router>
  </I18nextProvider>,
  document.getElementById("root")
);

Maintain your translation files under the Locales folder and make sure the namespace matches the one provided in the i18next store initialization.
Example translation here:

{
  "translations": {
    "title": "<0>Acerca de</0>",
    "intro": "<0>Esta es la página Acerca de.</0>"
  }
}

The text in the About page is translated as below using the locales:

import React from "react";
import { Trans } from "react-i18next";

const AboutPage = () => (
  <div>
    <Trans i18nKey="title">
      <h2>About</h2>
    </Trans>
    <Trans i18nKey="intro">
      <p>This is the About page.</p>
    </Trans>
  </div>
);

export default AboutPage;

The i18nKey matches the specific text to be translated under the namespace.


The HomePage.js code has a drop-down change language event that will change the translation in the About page when selected.

changeLang = lang => {
    const { i18n } = this.props;
    const { value } = lang;
    this.props.changeLang(lang);
    this.setState({ lang });
    i18n.changeLanguage(value);
  };

The app is hosted on IIS Server. Check out the post here on how to host react app on IIS.

There is also a branch in Github here, that explains the i18n legacy version usage i.e. till v9.

Fix your belly and change your life. Click here to know more.

Use react-router-dom for client side routing

Create a React app using the create-react-app command. For details, you can check out my other post to get started.

React-router-dom is a package used for Client side routing and you can install it from npm. Since React lets you create Single Page Applications (SPA), you need to use react-router to navigate between different components, changing the browser URL, modifying the browser history, and keeping the UI state in sync. SPA only modifies parts of a page instead of loading entire new pages from a Server.

react-router is the core package for routing, however for web you can use react-router-dom as shown in this demo.

The index.js file will use BrowserRouter from the API which uses the HTML5 History API e.g. http://abc/route/subroute

Our App component will be wrapped inside the BrowserRouter. The Router component can have only one child element.

Code sample below for index.js file:

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./components/App";
import * as serviceWorker from "./serviceWorker";
import { BrowserRouter as Router } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
ReactDOM.render(
  <Router>
    <App />
  </Router>,
  document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

App.js component code:

import React from "react";
import { Route, Switch } from "react-router-dom";
import HomePage from "./home/HomePage";
import AboutPage from "./about/AboutPage";
import Header from "./common/Header";
import PageNotFound from "./PageNotFound";
import CoursesPage from "./courses/CoursesPage";
function App() {
  return (
    <div className="container-fluid">
      <Header />
      <Switch>
        <Route exact path="/" component={HomePage} />
        <Route path="/courses" component={CoursesPage} />
        <Route path="/about" component={AboutPage} />
        <Route component={PageNotFound} />
      </Switch>
    </div>
  );
}
export default App;

Use the Switch component to match the first route that matches the location for rendering and does not look further.
exact property is used to match the URL path exactly. This will match the Home page for “/” path.

What happens when no route matches? We need to handle this scenario with a PageNotfound error page. Path attribute is not used for this Route but only the Component is required.

Link component is used to create links in your application. NavLink is used to add the style attributes to the active routes.

Code for Header section:

import React from "react";
import { NavLink } from "react-router-dom";
const Header = () => {
  const activeStyle = { color: "#F15B2A" };
  return (
    <nav>
      <NavLink to="/" activeStyle={activeStyle} exact>
        Home
      </NavLink>
      {" | "}
      <NavLink to="/courses" activeStyle={activeStyle}>
        Courses
      </NavLink>
      {" | "}
      <NavLink to="/about" activeStyle={activeStyle}>
        About
      </NavLink>
    </nav>
  );
};
export default Header;

You can find the complete code sample here in my github profile under the react-router-dom-demo branch.

Handle client side routes with IIS on Page refresh React App

When trying to handle Client side routing for your React App hosted on IIS say using react-router-dom, you might need to handle situations where users access specific sections of your App like http://testapp/courses. Users might even save these URLs in their favorites and try to access them later.

This problem is not known while debugging the App in localhost until you host the App on an IIS Server. Since your React App is a Single Page Application (SPA), the Server is unaware of any static files like courses and will give 404 error. To solve this, send all your requests back to IIS with URL rewrite to the index.html static file and let the React App handle the routing.

First install the URL Rewrite module on the IIS Server. Then create a web.config file for your App or create a new one with code as shown below:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
		<rewrite>
		  <rules>
			<rule name="ReactRouter Routes" stopProcessing="true">
			  <match url=".*" />
			  <conditions logicalGrouping="MatchAll">
				<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
			  </conditions>
			  <action type="Rewrite" url="index.html" />
			</rule>

		  </rules>
		</rewrite>
    </system.webServer>
</configuration>

This should work well for your App when the user tries to access a Client side route and refresh the page or when trying to access the route later.

Serve static json files on IIS hosted React App

When you have a React App hosted on IIS Server, you might need to access the static json files in your application for various purposes like json translations etc.

When you try to access these static files directly in browser or through code e.g. http://app/translation.json, you’ll get a 404 File Not Found error because the MIME type is not set by default in IIS. This is true for any other static file like woff files for fonts.

Under your Web.config file configuration section, place the below sample code within system.webServer tags.

<staticContent>
		<mimeMap fileExtension=".woff" mimeType="application/octet-stream" />
		<mimeMap fileExtension=".json" mimeType="application/json" />
	</staticContent>

The Web.config file is not available by default for a React App. You can either create one manually or make a change in the IIS Server Website configuration which will generate the Web.config file automatically and then you can add the above changes.

Host create-react-app on IIS Server

Install the latest version of nodejs from https://nodejs.org/ site.

Install create-react-app globally as below:

npm i -g create-react-app

Create the react-app in the folder of your choice on your local machine:

create-react-app hell-world-app

Modify the index.js file as shown below:

import React from "react";
import ReactDOM from "react-dom";

const element = <h1>Hello World</h1>;

//console.log(element);

ReactDOM.render(element, document.getElementById("root"));

Test the app on localhost:

npm start

Build the production optimized app:

npm run-script build

Copy the contents of the build folder and paste it on the Server at the location where you want to host it e.g. C:\data\testreactapp. Open IIS on the Server and add Website by right-clicking on Sites as shown below. Give it the required hostname or port to test it in browser e.g. http://helloworldapp.

The index.html file is already present at the Physical path created by the build which the IIS Server will look for as the default document.

Authentication can be Anonymous for testing purpose. However, please change as per your requirement. The AppPool by default runs with CLR 4.0 version and Integrated mode.

This approach is especially helpful when you have a back-end .Net Web API hosted on the same IIS Server to improve speed.

Create basic ReactJS app using VS Code

We’ll be using VS Code editor to create the basic app to get started.

Install Extensions like Simple React Snippets and Prettier in VS Code to help with writing basic react snippets faster and properly formatted.

From the VS Code menu, Select Preferences-> User settings->search for Format on Save. This will help format the code on saving the file using the Prettier extension.

You’ll also need the latest stable versions of Node and npm installed.

Enter in Command Prompt on Windows or Command Line on Mac the below commands:

npm i -g create-react-app@1.5.2

This will install the Create React package globally.

Enter the below command to create the App in the directory of your choice:

create-react-app react-app

What is JSX?

JSX is a syntax extension to JavaScript. It is recommended using it with React to describe what the UI should look like, however it is not necessary. JSX produces React “elements”. JSX code is compiled by Babel, which is a modern JavaScript compiler that compiles JSX code to corresponding React code that Browsers can understand. The render method may contain JSX code.

Sample React class looks like below:

class MyComp {
	state = {};
	render() {
	
	}
}

State holds the data to be displayed in the UI and render method is responsible for describing what the UI should look like i.e. a React element.
This React element is a plain Javascript object that maps to a DOM element.

How React reacts!

It is a light-weight representation of the DOM in memory which is also called Virtual DOM. This virual DOM is cheap to create. When we change the state of a component, we get a new React element. React will then compare this element and it’s children with the previous one and it figures out what is changed. Then it will update a part of the real DOM to keep it in sync with the Virtual DOM. Hence we don’t need to use the DOM API to manipulate it or attach event handlers with the DOM elements. React will automatically update the DOM to match that state.

React simply reacts to the state change and updates the DOM.

index.js is the entry point for the default created application.

registerServiceWorker.js serves assets from a local cache in the Production environment. It may change or disappear in the future.

index.html has a div element as a container with id “root” that will render all the React Components.

Run below command in the integrated terminal in VS Code, this will run the app using the light-weight development Server installed with create command above using default port 3000:

npm run start

You can access the app using http://localhost:3000 in a browser as below:

Delete all the existing files in the src folder. Create a new file called index.js.

Make changes to the file and save it. It will refresh in the browser which is called hot module reloading.

Modify index.js file as below:

import React from "react";
import ReactDOM from "react-dom";

const element = <h1>Hello World</h1>;

console.log(element);

This Object as shown in the browser dev tools above is part of the Virtual DOM.

So, whenever this Object changes, React will get a new React element, it will then compare this element with the previous one. It will figure what is changed. Then it will reach out to the real DOM and update it accordingly.

Remove the line console.log in the index.js file with below and save:

ReactDOM.render(element, document.getElementById("root"));

Run the application and inspect the element in the browser to see how the element is rendered.