As explained in the title, I am getting the error Expected onClick listener to be a function, instead got type object
But I am unable to understand why this isnt working. as far as I know, the onClick listener IS a function.
Here's, the CharacterList Component where the error comes from
import React,{Component} from 'react';
import {connect} from 'react-redux';
import {addCharacterById} from '../actions';
import {bindActionCreators} from 'redux';
class CharacterList extends Component{
render(){
//console.log('name : ',this.props.characters[2].name);
return(
<div>
<h3>Characters</h3>
<ul>
{this.props.characters.map((character)=>{
return(<li key={character.id}>{character.name}
<div
onClick={this.props.addCharacterById(character.id)}
>+</div>
</li>);
})}
</ul>
</div>
)
}
}
function mapStateToProps(state){
return {
characters:state
}
}
export default connect(mapStateToProps,{addCharacterById})(CharacterList);
And here's the action creator
export const ADD_CHARACTER='ADD_CHARACTER';
export function addCharacterById(id){
var action ={
type:ADD_CHARACTER,
id
}
return action;
}
So, what do you guys think? what is the problem here?
The problem is that you're invoking the function immediately and then what's left is the return value, which might not be a function!
What you can do instead is wrap that function call inside an arrow function to solve your problem. It'll call the inner function once you onClick:
import React,{Component} from 'react';
import {connect} from 'react-redux';
import {addCharacterById} from '../actions';
import {bindActionCreators} from 'redux';
class CharacterList extends Component{
render(){
//console.log('name : ',this.props.characters[2].name);
return(
<div>
<h3>Characters</h3>
<ul>
{this.props.characters.map((character)=>{
return(<li key={character.id}>{character.name}
<div
onClick={() => this.props.addCharacterById(character.id)}
>+</div>
</li>);
})}
</ul>
</div>
)
}
}
function mapStateToProps(state){
return {
characters:state
}
}
export default connect(mapStateToProps,{addCharacterById})(CharacterList);
There's diferent ways of doing this, you could for example bind the parameter to the function, like:
{this.props.characters.map((character)=>{
return(<li key={character.id}>{character.name}
<div
onClick={this.props.addCharacterById.bind(null, character.id)}
>+</div>
</li>);
})}
Just shared as an example, so that you understand what's going on and why the first approach is more readable. You may want to look into why .bind in render is a bad practice by reading the article https://ryanfunduk.com/articles/never-bind-in-render/
onClick={this.props.addCharacterById(character.id)}
this part of your code will execute immediately upon render()
is call what you may want to do instead is:
onClick={(e)=> {this.props.addCharacterById(e, character.id)}}
Remember the first parameter pass to onClick
is the click event.
Don't directly call functions in onClick
event. It will recursively call the method. So make the onClick
input as a callback method.
Change this line
onClick={this.props.addCharacterById(character.id)}
to
onClick={() => this.props.addCharacterById(character.id)}
There are some things that you need to change if you want to have good practices.
First, add mapDispatchToProps
function.
import { bindActionCreators } from 'redux';
...
function mapDispatchToProps(dispatch) {
return {
addCharacterById: bindActionCreators(addCharacterById, dispatch)
};
}
Second, the event handler could be:
onClick={() => this.props.addCharacterById(character.id)}
Third, export your component:
export default connect(mapStateToProps, mapDispatchToProps)(CharacterList);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With