Reputation: 879
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
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
Reputation: 795
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