How to use Idle-timeout in React application

Idle timeout is one of the common features in every web application. In this post, we can see how we can implement this feature in the React application.

What is Idle Timeout

Idle timeout is the amount of time the user or client remains inactive on the web application. This is the time that the user doesn’t do anything on the app or away from the computer. It is typical for high-risk web apps to have 2–5 minutes of idle time and low-risk web apps to have 15–30 minutes of idle time before logging out the user.

Tip: Share and reuse React components for faster development and more consistent UI/UX

Use Bit to share and reuse React components across different projects. Collaborate on shared components as a team to build apps faster together. Let Bit do the heavy lifting so you can easily publish, install and update your individual components without any overhead.

Why do we need it

Most of the cases we need an idle timeout in the web apps for security reasons and to avoid unnecessary calls to the API. The web app is vulnerable to security attacks. The longer it is idle, the more time for the attacker to inject some malicious code or hijack the session.

In another case, imagine a scenario where there are calls to be made to the backend API for every 30 sec or 1 min to retrieve the updated data. There is no need to make those calls if the user is away from the computer. We can avoid these unnecessary calls with the idle timeout feature.

Example Project

Here is an example project of implementing idle-timeout in React. You can clone this project and run it o your machine.

// clone the project
git clone https://github.com/bbachi/react-idle-timeout.git
// install dependencies and start the project
npm install
npm start

Here are the libraries used for this project.

This is a simple project which has two screens: login screen and dashboard screen. When you log in with any username and password, dashboard displays with some user table. If you are idle for 5 seconds we are going to let the user know with a modal window and if the user is still idle after 5 seconds, the user will be logged out.

This is image title

How To Find Out User is Idle

Actually we are using the library called ng-idle But, I want to show how we can detect the user is idle without any libraries. We have Keyboard events and mouse events from the DOM API which we can use to detect the users are idle or not. Basically, when there is no mouse or keyboard events fired, we can conclude that the user is idle.

// keyboard events link
https://developer.mozilla.org/en-US/docs/Web/Events#Keyboard_events

// mouse events
https://developer.mozilla.org/en-US/docs/Web/Events#Mouse_events

Here is some sample code with plain vanilla Javascript. There are only two events onmousemove and onkeypress used here but, we can use other events as well.

var inactivityTime = function () {
    var time;
    window.onload = resetTimer;
		
// DOM Events
    document.onmousemove = resetTimer;
    document.onkeypress = resetTimer;
		
    function resetTimer() {
        clearTimeout(time);
        time = setTimeout(logout, 3000)
    }
};

Implementation

Let’s see how we can implement the idle timeout feature in the React application with the react-idle-timer library. As a first step, we need to install react-idle-timer dependencies in the existing application.

npm i react-idle-timer --save

Once installed we have to import these modules in the Layout.js file.

import React from 'react'
import { Switch, Route } from 'react-router-dom'
import IdleTimer from 'react-idle-timer';
import { DashboardPage } from './dashboard/Dashboard'
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css'

class Layout extends React.Component {

    constructor(props){
        super(props)
    }

    render(){
      const { match } = this.props
      return(
        <div className="">
            <Switch>
                <Route 
                    exact path={`${match.path}dashboard`}
                    render={(props) => <DashboardPage {...props} /> }/>
                />
            </Switch>
        </div>
        
      )
   }

 }

 Layout.propTypes = {
     match: PropTypes.any.isRequired,
     history: PropTypes.func.isRequired
 }

export default Layout

Since LayOut is the main component once logged in from the Login, we need to implement this logic in it. We are setting the Idle time 5 seconds which means we will get a warning message after 5 seconds of inactivity and timeout as 5 seconds which means it will be timed out within another 5 seconds.

I set both idle time and timeout time to 5 seconds for the demonstration purpose. Usually, these times are longer in the actual environments, typically it is around 15 mins.

import React from 'react'
import { Switch, Route } from 'react-router-dom'
import IdleTimer from 'react-idle-timer';
import { DashboardPage } from './dashboard/Dashboard'
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css'

class Layout extends React.Component {

    constructor(props){
        super(props)

        this.state = {
            timeout:1000 * 5 * 1,
            showModal: false,
            userLoggedIn: false,
            isTimedOut: false
        }

        this.idleTimer = null
        this.onAction = this._onAction.bind(this)
        this.onActive = this._onActive.bind(this)
        this.onIdle = this._onIdle.bind(this)
    }

    _onAction(e) {
      console.log('user did something', e)
      this.setState({isTimedOut: false})
    }
   
    _onActive(e) {
      console.log('user is active', e)
      this.setState({isTimedOut: false})
    }
   
    _onIdle(e) {
      console.log('user is idle', e)
      const isTimedOut = this.state.isTimedOut
      if (isTimedOut) {
          this.props.history.push('/')
      } else {
        this.setState({showModal: true})
        this.idleTimer.reset();
        this.setState({isTimedOut: true})
      }
      
    }

    render(){
      const { match } = this.props
      return(
        <>
          <IdleTimer
            ref={ref => { this.idleTimer = ref }}
            element={document}
            onActive={this.onActive}
            onIdle={this.onIdle}
            onAction={this.onAction}
            debounce={250}
            timeout={this.state.timeout} />

            <div className="">
                <Switch>
                    <Route 
                        exact path={`${match.path}dashboard`}
                        render={(props) => <DashboardPage {...props} /> }/>
                    />
                </Switch>
            </div>
        </>
      )
   }

 }

 Layout.propTypes = {
     match: PropTypes.any.isRequired,
     history: PropTypes.func.isRequired
 }

export default Layout

I want to show a modal window whenever the user is idle and give some options for the user to logout or stay as shown in the figure.

This is image title

For this, we are using a react-bootstrap modal component. We need to create a separate component and import that component in the Layout.js as shown below.

This is a stateless component which takes props and returns modal window based on the properties provided to this component. Notice that we are importing required components Modal and Button from react-bootstrap.

import React from 'react';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';

export const IdleTimeOutModal = ({showModal, handleClose, handleLogout, remainingTime}) => {

    return (
        <Modal show={showModal} onHide={handleClose}>
            <Modal.Header closeButton>
            <Modal.Title>You Have Been Idle!</Modal.Title>
            </Modal.Header>
            <Modal.Body>You Will Get Timed Out. You want to stay?</Modal.Body>
            <Modal.Footer>
            <Button variant="danger" onClick={handleLogout}>
                Logout
            </Button>
            <Button variant="primary" onClick={handleClose}>
                Stay
            </Button>
            </Modal.Footer>
        </Modal>
    )
}

Once created, we need to import this file in the Layout.js and pass the required props.

// Idletimout Modal part of Layout.js component

import { IdleTimeOutModal } from './modal/IdleModal'

class Layout extends React.Component {

    constructor(props){
        super(props)

        this.state = {
            timeout:1000 * 5 * 1,
            showModal: false,
            userLoggedIn: false,
            isTimedOut: false
        }
        this.handleClose = this.handleClose.bind(this)
        this.handleLogout = this.handleLogout.bind(this)
    }

    handleClose() {
      this.setState({showModal: false})
    }

    handleLogout() {
      this.setState({showModal: false})
      this.props.history.push('/')
    }

    render(){
      const { match } = this.props
      return(
        <>
            <div className="">
                <Switch>
                    <Route 
                        exact path={`${match.path}dashboard`}
                        render={(props) => <DashboardPage {...props} /> }/>
                    />
                </Switch>
                
                <IdleTimeOutModal 
                    showModal={this.state.showModal} 
                    handleClose={this.handleClose}
                    handleLogout={this.handleLogout}
                />
            </div>
        </>
      )
   }

 }

 Layout.propTypes = {
     match: PropTypes.any.isRequired,
     history: PropTypes.func.isRequired
 }

export default Layout

Along with this, we don’t want the idle timeout feature on the login screen. For this, I created Layout.js which has all the routes besides login. Here is the App component which loads the Layout component for the route /dashboard and the Layout component loads the actual DashboardPage component.

import React from 'react'
import { BrowserRouter, Switch, Route } from 'react-router-dom'
import { Header } from './header/Header'
import { Footer }from './footer/Footer'
import Layout from './Layout'
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css'
import { LoginPage } from './login/Login'

class App extends React.Component {

    constructor(props){
        super(props)
    }

    render(){

      return(
        <div className="App">
          <Header />
          <BrowserRouter>
            <Switch>
              <Route exact path="/" component={LoginPage} />
              <Route path='/' render={(props) => <Layout {...props} /> } />
            </Switch>
          </BrowserRouter>
          <Footer />
        </div>
      )
   }

 }

 App.propTypes = {
     match: PropTypes.any.isRequired,
     history: PropTypes.func.isRequired
 }

export default App
// routung part of Layout Page

<div className="">
  <Switch>
      <Route 
          exact path={`${match.path}dashboard`}
          render={(props) => <DashboardPage {...props} /> }/>
      />
  </Switch>
</div>

Here is the complete Layout.js file. If you notice the function _onIdle() at line 40. We are logging out the user if he is idle even after showing the modal window. We can accomplish this in a number of ways but I used isTimedOut the property in the state. When the user is idle for the first time, I am showing the modal window and setting this flag as true in the state and reset the timer. If the user is still idle, I am logging the user out based on the flag isTimedOut

If the user responds to that modal window, we are using handleLogout and handleClose functions to take the appropriate action at lines 53 and 57 respectively.

import React from 'react'
import { Switch, Route } from 'react-router-dom'
import IdleTimer from 'react-idle-timer';
import { DashboardPage } from './dashboard/Dashboard'
import { IdleTimeOutModal } from './modal/IdleModal'
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css'

class Layout extends React.Component {

    constructor(props){
        super(props)

        this.state = {
            timeout:1000 * 5 * 1,
            showModal: false,
            userLoggedIn: false,
            isTimedOut: false
        }

        this.idleTimer = null
        this.onAction = this._onAction.bind(this)
        this.onActive = this._onActive.bind(this)
        this.onIdle = this._onIdle.bind(this)
        this.handleClose = this.handleClose.bind(this)
        this.handleLogout = this.handleLogout.bind(this)
    }

    _onAction(e) {
      console.log('user did something', e)
      this.setState({isTimedOut: false})
    }
   
    _onActive(e) {
      console.log('user is active', e)
      this.setState({isTimedOut: false})
    }
   
    _onIdle(e) {
      console.log('user is idle', e)
      const isTimedOut = this.state.isTimedOut
      if (isTimedOut) {
          this.props.history.push('/')
      } else {
        this.setState({showModal: true})
        this.idleTimer.reset();
        this.setState({isTimedOut: true})
      }
      
    }

    handleClose() {
      this.setState({showModal: false})
    }

    handleLogout() {
      this.setState({showModal: false})
      this.props.history.push('/')
    }

    render(){
      const { match } = this.props
      return(
        <>
          <IdleTimer
            ref={ref => { this.idleTimer = ref }}
            element={document}
            onActive={this.onActive}
            onIdle={this.onIdle}
            onAction={this.onAction}
            debounce={250}
            timeout={this.state.timeout} />

            <div className="">
                <Switch>
                    <Route 
                        exact path={`${match.path}dashboard`}
                        render={(props) => <DashboardPage {...props} /> }/>
                    />
                </Switch>
                
                <IdleTimeOutModal 
                    showModal={this.state.showModal} 
                    handleClose={this.handleClose}
                    handleLogout={this.handleLogout}
                />
            </div>
        </>
      )
   }

 }

 Layout.propTypes = {
     match: PropTypes.any.isRequired,
     history: PropTypes.func.isRequired
 }

export default Layout

Summary

  • Idle timeout is one of the common features in every web application yet a very important one.
  • We can avoid malicious attacks and unnecessary backend API calls with this feature.
  • We can actually detect the idle user with the help of DOM events: keyboard events and mouse events.
  • For React applications, we can use the react-idle-timer library.
  • It’s always good to let the user know with the modal popup that he/she has been idle before logging them out.

You may also like: Angular vs React vs Vue: Which one will be popular in 2020.

Conclusion

Idle timeout feature is the most common yet powerful feature to avoid malicious attacks and unnecessary backend calls.

Thank for visiting and reading this article! I’m highly appreciate your actions! Please share if you liked it!

#reactjs #javascript #web-development

How to use Idle-timeout in React application
4 Likes336.10 GEEK