cheslijones
cheslijones

Reputation: 9194

Calling action inside function inside componentWillMount causing Cannot read property of undefined

Pretty new to React and ES6 conventions. I am trying to call an action from within a function that is inside of a componentWillMount(). This is resulting in an Uncaught TypeError: Cannot read property 'signoutUser' of undefined. Not quite sure how to resolve it, tried binding this, which did resolve the problem.

This my code in its current form:

// left_site.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { signoutUser } from '../actions/authentication';

export default function(ComposedComponent) {
    class LeftSite extends Component {

        constructor(props, context) {
            super(props, context);
        }

        componentWillMount() {

            var timerLeft = setInterval(timer, 1000);

            function timer() {

                if ((sessionStorage.getItem('timer') > 0) && 
                    (new Date().getTime() - sessionStorage.getItem('timer') > 5000)) {
                    this.props.signoutUser();
                } else {
                    sessionStorage.setItem('timer', new Date().getTime());
                }
            }
        }

        render() {
            return <ComposedComponent {...this.props} />
        }
    }

    return connect( null, { signoutUser })(LeftSite);
}

To explain what is going on, the company wants the user to automatically be logged out if they navigate away from any of the protected routes on the domain to another domain. One idea was to create a "heartbeat" that committed the time to sessionStorage every second so long as the user is on a protected route on the domain. The idea is if the navigate to another domain, then try to come back, if the difference in the last stored time and the current time is greater than 5000ms, it will automatically signout.

There may be a better way to do this, but I couldn't think of one that 1) didn't violate privacy or 2) wouldn't trigger the logout with a refresh, unbind for example.

The left_site.js is a HOC--I also have a required_login.js HOC to reroute to the login page if someone tries to access the protected route without authentication--so my protected routes are wrapped in component={LeftSite(RequireAuth(Home))}.

LeftSite is running fine, however when the conditional evaluates to true and it tries to trigger the this.props.signoutUser(); the error comes up.

Upvotes: 0

Views: 469

Answers (2)

Panther
Panther

Reputation: 9408

Function timer is not bound to class. When it is executed at regular interval, the execution context changes. You have to bind the function before use. Also make sure you clear the interval at proper time, or when the component unmounts. I suggest you write this way

 timer = () => {
   if ((sessionStorage.getItem('timer') > 0) && 
        (new Date().getTime() - sessionStorage.getItem('timer') > 5000)) {
          this.props.signoutUser();
   } else {
      sessionStorage.setItem('timer', new Date().getTime());
   }
}

 componentWillMount() {
   this.timerLeft = setInterval(this.timer, 1000)
 }

 componentWillUnmount() {
   clearInterval(this.timerLeft);
 }

Upvotes: 1

elas
elas

Reputation: 815

You need to bind this to the timer function. The easier and recommended way to do this is by defining an arrow function for your timer, like this:

export default class MyComponent extends React.Component {
  componentWillMount() {
    const timer = () => {
      console.log(this.props); // should be defined
    };

    const timerLeft = setInterval(timer, 1000);
  }
}

This works because with arrow functions, this is set to the this of the enclosing context.

Upvotes: 1

Related Questions