Search code examples
javascriptreactjsreduxreact-redux

Expected onClick listener to be a function, instead got type object - react redux


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?


Solution

  • 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/