Kantarello
Kantarello

Reputation: 3

Passing a function as a prop in react, Cannot read property 'edit' of undefined

I'm learning ReactJS and I'm creating a editable Pokémon list based on this guide.

When I try to pass a function to edit a list item (at this point I want to click the item and get the name), I get TypeError: Cannot read property 'edit' of undefined on the following line of the addPokemon function: onClick={() => this.edit(pokemon.name)}

Code:

PkmnForm

import React, { Component } from "react";
import PkmnList from "./PkmnList";

class PkmnForm extends Component {

  static types = [
    'Bug',
    'Dragon',
    'Ice',
    'Fighting',
    'Fire',
    'Flying',
    'Grass',
    'Ghost',
    'Ground',
    'Electric',
    'Normal',
    'Poison',
    'Psychic',
    'Rock',
    'Water'
    ]

  constructor(props) {
    super(props);

    this.state = {
      name: '',
      type: '',
      pokemons: [],
      caught: false,
    };
  }

  handleSubmit = (event) => {
    var pokemon = {
      name: this.state.name,
      type: this.state.type,
      caught: this.state.caught
    };

    this.setState({
      name: '',
      type: '',
      caught: false,
      pokemons: this.state.pokemons.concat(pokemon)
    });
    event.preventDefault()

  }

  handleChange = (event) => {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  handleTypeChange = (event) => {
    this.setState({
      type: event.target.value,
    })
  }

  editPokemon(name) {
    console.log(name);
  }

  render() {
    return (
        <div>
          <div>
            <h1>Register a Pokémon</h1>

            <form onSubmit={this.handleSubmit}>

              <input 
                required
                placeholder=" Name"
                name="name"
                onChange={this.handleChange}
                value={this.state.name} 
              />
              <br />
              <select 
                name="type"
                required
                value={this.state.type}
                onChange={this.handleChange}
              >
                <option value='' disabled>Type</option>
                {PkmnForm.types.map(
                  optionValue => (
                    <option
                      key={optionValue}
                      value={optionValue}
                    >
                      {optionValue}
                    </option>
                  )
                )}
              </select>
              <br />
              <label>
                Caught
                <input
                  name="caught"
                  type="checkbox"
                  checked={this.state.caught}
                  onChange={this.handleChange}
                />
              </label>
              <br />
              <button type="submit">Add Pokemon</button> 
            </form>
          </div>

          <div>
            <PkmnList
              pokemons={this.state.pokemons}
              edit={this.editPokemon}
            />
          </div>
        </div>
    );
  }
}

export default PkmnForm;

PkmnList

import React, { Component } from 'react';

class PkmnList extends Component {

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

  edit(name) {
    this.props.edit(name);
  }

  addPokemon(pokemon) {
    return <li 
              onClick={() => this.edit(pokemon.name)}
              key={pokemon.name}
            >
              {pokemon.name} – {pokemon.type} {pokemon.caught ? '(caught)' : ''}
            </li>
  }

  render() {
    var pokemons = this.props.pokemons;
    var listItems = pokemons.map(this.addPokemon);
    return (
      <ul>
        {listItems}
      </ul>
    );
  }
}

export default PkmnList;

Thanks :-)

Upvotes: 0

Views: 434

Answers (2)

smsegal
smsegal

Reputation: 1

You need to bind editPokemon like so:

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

Or you can also use arrow functions, which have proper scoping:

editPokemon = (pokemon) => {
...
}

Upvotes: 0

poke
poke

Reputation: 388383

The problem is in this line:

var listItems = pokemons.map(this.addPokemon);

Here, you are passing this.addPokemon as the function to map. But that function is not bound to this, so inside of it, this is not available.

You either have to bind it explicitly by calling .bind(this), just like you did with the edit function:

var listItems = pokemons.map(this.addPokemon.bind(this));

Or you can pass an arrow function that calls the method:

var listItems = pokemons.map(x => this.addPokemon(x));

Upvotes: 1

Related Questions