YSA
YSA

Reputation: 879

What is the difference between passing a function as a prop with or without parentheses in React?

This is probably something that I should know but I don't quite understand the behavior of my component when I pass a function without parentheses. Here's my component code.

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';

import AppBar from 'material-ui/AppBar';
import LoginButton from './LoginButton';
import LogoutButton from './LogoutButton';

class Header extends Component {
  renderButton() {
    switch (this.props.auth) {
      case null:
        return
      case false:
        return <LoginButton />
      default:
        return <LogoutButton />
    }
  }



handleTitleClick() {
    return(
      <Link to={this.props.auth ? '/classes' : '/'}>
        QueueMe
      </Link>
    );
  }

  render() {
    const styles = {
      title: {
        cursor: 'pointer',
      },
    };

    return(
      <AppBar
        title={<span style={styles.title}>QueueMe</span>}
        onTitleClick={this.handleTitleClick()}
        iconElementRight={this.renderButton()}
        showMenuIconButton={false}
      />
    );
  }
}

/*
 * @input: redux state
 * Allows the component to access certain piece of the state as props
 * Reducers determine the key in the state
 */
function mapStateToProps(state) {
  return { auth: state.auth };
}

export default connect(mapStateToProps)(Header);

For my onTitleClick property in <AppBar>, I get the expected behavior when I pass it handleTitleClick() but when I pass it handleTitleClick and click it, I get an error that says Cannot read property 'auth' of undefined. What exactly is the difference here that causes the handleTitleClick not to be aware of the state?

Upvotes: 1

Views: 2714

Answers (2)

John Detlefs
John Detlefs

Reputation: 182

Good question! There are a few things wrong going on here. Javascript this can be a real pain. The problem is that your functions are not bound.

When you write onTitleClick={this.handleTitleClick()} you are immediately invoking the function at compile time. When you pass it handleTitleClick when you are providing an unbound function, it has no this defined.

There are two potential solutions, you can either define

handleTitleClick = (event) =>
    return(
      <Link to={this.props.auth ? '/classes' : '/'}>
        QueueMe
      </Link>
    );
  }

This makes handleTitleClick an arrow function, arrow functions bind their this to the closure that they were created in.

If you don't like using the IIFE way, you can always use

constructor(props) {
   super(props)
   this.handleTitleClick = this.handleTitleClick.bind(this)
}

Check this out if you're still stuck. https://medium.freecodecamp.org/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56

Upvotes: 5

You need to bind this to your component.

constructor(props){
  super(props);
  this.handleTitleClick = this.handleTitleClick.bind(this);
}

After this you can call without the parenthesis. Actually when you call with parenthesis you actually execute the function on render which may not actually the thing you want. You want to call the function only on click not on render. So use without parenthesis and bind your call in the constructor.

<AppBar
    ...
    onTitleClick={this.handleTitleClick}
    ...
  />

Upvotes: 2

Related Questions