This.props is not a function. Passing methods as props?

Hi! I’m having a bit of an issue completing the Jammming project within the WebDev course (https://bit.ly/2TBz9xz), I’m about halfway through and I am getting the following error:

**TypeError: this.props.onAdd is not a function**
addTrack(){
var track = this.props.track;
this.props.onAdd(track);
};
Project Breakdown
1. addTrack() defined in <App /> and bound using this.addTrack = this.addTrack.bind(this);
2. passed to SearchResults via <SearchResults onAdd={this.addTrack} />
3. passed to Tracklist via <Tracklist onAdd={this.props.onAdd} />
4. passed to Track via <Track onAdd={this.props.onAdd} />
5. passed to a button via <button onClick={this.addTrack} />
Here is where the method is invoked in App.js
addTrack(track){
    var tracks = this.state.playlistTracks;
    if (tracks.find(savedTrack => savedTrack.id === track.id)){
    return;
    }
    tracks.push(track);
    this.setState({
    playlistTracks: tracks
    })
  };

Files:

App.js
import React from 'react';
import SearchBar from '../SearchBar/SearchBar';
import SearchResults from '../SearchResults/SearchResults';
import Playlist from '../Playlist/Playlist';
import './App.css';

class App extends React.Component{
  constructor(props){
    super(props);
     
      this.state = {
      playlistName: "Playlist 01",
      playlistTracks:[{
        name: "Track99",
        artist: "TrackArtist99",
        album: "TrackAlbum99",
        id: "T99",
      },],
      searchResults: [{
        name: "Track01",
        artist: "TrackArtist01",
        album: "TrackAlbum01",
        id: "T01",
      },{
        name: "Track02",
        artist: "TrackArtist02",
        album: "TrackAlbum02",
        id: "T02",
      },{
        name: "Track03",
        artist: "TrackArtist03",
        album: "TrackAlbum03",
        id: "T03",
      },{
        name: "Track04",
        artist: "TrackArtist04",
        album: "TrackAlbum04",
        id: "T04",
      },]
    };

    this.addTrack = this.addTrack.bind(this);
  };

  addTrack(track){
  var tracks = this.state.playlistTracks;
  if (tracks.find(savedTrack => savedTrack.id === track.id)){
    return;
  }
  tracks.push(track);
  this.setState({
    playlistTracks: tracks
  })
  };

  render(){
    return (
            <div>
  <h1>Ja<span className="highlight">mmm</span>ing</h1>
  <div className="App">
    <SearchBar />
    <div className="App-playlist">
     <SearchResults onAdd={this.addTrack} searchResults={this.state.searchResults}/>
     <Playlist playlistName={this.state.playlistName} playlistTracks={this.state.playlistTracks}/>
  
    </div>
  </div>
</div>
      );
  }
};

export default App;
SearchResults.js
import React from 'react';
import Tracklist from '../Tracklist/Tracklist';
import './SearchResults.css';

class SearchResults extends React.Component{
	constructor(props){
		super(props);
	}
	render(){
		return(
			<div className="SearchResults">
  			<h2>Results</h2>

  			<Tracklist isRemoval={false} onAdd={this.props.onAdd} tracks={this.props.searchResults} />
			</div>
			);
	}
};

export default SearchResults;
"Tracklist.js
import React from 'react';
import './Tracklist.css';
import Track from '../Track/Track';
class Tracklist extends React.Component{
	
	render(){		
			return(
			<div className="Tracklist">
			{
				this.props.tracks.map(track => {
					return <Track onAdd={this.props.onAdd} track={track} key={track.id} />
				})
			}
			</div>
		);
	}
};

export default Tracklist;

Track.js
import React from 'react';
import './Track.css';
import { PropTypes } from 'react'
class Track extends React.Component{
	constructor(props){
		super(props);
		this.addTrack = this.addTrack.bind(this);
		this.renderAction = this.renderAction.bind(this);	
	}
	
	renderAction(){

		var buttonContent = '';
			if(this.props.isRemoval){
				buttonContent = "-";
			} else {
				buttonContent = "+";
			};
		return(
			<button className="Track-action" onClick={this.addTrack}>{buttonContent}</button>
			);
	};

	addTrack(){
		var track = this.props.track;
		this.props.onAdd(track);
	};
	
	render(){

		return(
			<div className="Track">
				<div className="Track-information">
					<h3>{this.props.track.name}</h3>
					<p>{this.props.track.artist} | {this.props.track.album}</p>
				</div>
				{this.renderAction()}
			</div>
		);
	}
};

export default Track;
Playlist.js
import React from 'react';
import Tracklist from '../Tracklist/Tracklist';
import './Playlist.css';
class Playlist extends React.Component{
	render(){
		return(
			<div className="Playlist">
	      <input defaultValue={'New Playlist'}/>
	     	<Tracklist tracks={this.props.playlistTracks} />
	 	    <button className="Playlist-save">SAVE TO SPOTIFY</button>
			</div>
	  );
  }
};

export default Playlist;

Any help would be greatly appreciated, Thanks.

Did you mean for it to be a function?
Did you define it yourself? If so that’s where the problem is right?
Or if you didn’t mean for it to be a function, don’t treat it like one
Or if it should should be a function, and you didn’t define it, then define it

Yes, the prop is meant to act as a function. It was defined as a method in the parent component. Aside from binding the method using this.example.bind(this) in the constructor, and passing it as a prop using <Example exampleProp={this.example}/>, is there anything else I need to do to properly define it as a function?

The method works fine within the first child component, I can pass it arguments like a regular method:

<div>
{this.props.example("Example input")}
</div>

but after being passed as a prop to a second child using <ExampleChild example={this.props.example} /> it stops working as a function and no longer accepts arguments.

So where are you defining that prop? What makes you say that it’s there?

Okay, so far so good. Who’s that, tracklist?

Yeah, it’s there. But who’s providing this.props.onAdd here then?

… well, where is it?
So, there was supposed to be a function there, nobody defined it … define it… done?

I may very well be missing things because idk react, so you know better than me where it’s supposed to come from. Follow it.

Going one step further up, it appears again:

<Tracklist isRemoval={false} onAdd={this.props.onAdd} tracks={this.props.searchResults} />

Does that implicitly get included if you do not mention it in children? But if so, then why bothering mentioning it in any of the intermediaries, so I’m guessing, not? But idk.

Seems unlikely. More likely, you’re using a different value altogether.

1 Like

I found it I did not bind the addTrack function thats way I got typeError