0

I'm having an issue with displaying an array of Pokemon moves that I've retrieved from the database (using a rails/react/redux). The strange thing is that when I display the entire array without trying to use join on it, everything's fine. But when I try to use join on it, I get this error message: Uncaught TypeError: Cannot read property 'join' of undefined. And when I display the entire array variable as is, without modifying anything, it does display.

I've gone through my code and I've confirmed that the routing is fine and the right action creators are being dispatched. I've also gone through my reducers and I can confirm that they are using the right action constants to make decisions as to what the new state object is. The only thing that I can think of is that maybe the problem lies in one of these places below:

// the component that renders my Pokemon details onto the page

class PokemonDetail extends React.Component {
  componentDidMount() {
    this.props.requestSinglePokemon(this.props.match.params.pokemonId);
  }

  componentDidUpdate(prevProps) {
    if (prevProps.match.params.pokemonId !== this.props.match.params.pokemonId) {
      this.props.requestSinglePokemon(this.props.match.params.pokemonId);
    }
  }

  render() {
    const { pokemon } = this.props;
    if (!pokemon) return null;
    return (
      <section className='pokemon-detail'>
        <figure>
          <img src={ pokemon.image_url } alt={ pokemon.name } />
        </figure>
        <ul>
          <li><h1>{ pokemon.name }</h1></li>
          <li>Type: { pokemon.poke_type }</li>
          <li>Attack: { pokemon.attack }</li>
          <li>Defense: { pokemon.defense }</li>
          <li>Moves: { pokemon.moves.join(', ') }</li>
        </ul>
      </section>
    );
  }
}

// the reducer

const pokemonReducer = (state = {}, action) => {
  Object.freeze(state);
  let poke;
  switch(action.type) {
    case RECEIVE_ALL_POKEMON:
      return Object.assign({}, state, action.pokemon);
    case RECEIVE_SINGLE_POKEMON:
      poke = action.payload.pokemon;
      return Object.assign({}, state, { [poke.id]: poke });
    default:
      return state;
  }
}

// the action creators

export const requestSinglePokemon = id => dispatch => {
  dispatch(startLoadingSinglePokemon());
  return APIUtil.fetchSinglePokemon(id).then(pokemon => dispatch(receiveSinglePokemon(pokemon)));
}

export const receiveSinglePokemon = payload => ({
  type: RECEIVE_SINGLE_POKEMON,
  payload
});

// the backend APIUtil method

export const fetchSinglePokemon = id => (
  $.ajax({
    method: 'GET',
    url: `api/pokemon/${id}`
  })
);

It's the strangest thing...when I do { JSON.stringify(pokemon.moves) } I can literally see that it is an array of strings (of Pokemon moves). And even when I just display it like this, { pokemon.moves }, it seems to be correctly grabbing the right Pokemon; the name, type, attack, and defense text is all there and the moves is also displayed (although not correctly formatted).

NOTE: Upon observing my log (it shows me the action creators that are dispatched when I interact with the page), the error with join seems to pop up before (or instead?) of the action creator that should have been dispatched (i.e. I see that the START_LOADING_SINGLE_POKEMON action is never dispatched before this error pops up)

1 Answer 1

1

for the first render of your app , no actions have been dispatched. componentDidMount occurs after initial render. hence, unless the parent component of "PokemonDetail" is passing an intial pokemon object with key "moves " whose value is of type array, it would casue this error for first render.

you can either have conditional rendering inside "PokemonDetail" component based on "Pokemon" object or have inital state of "pokemon" contain moves array in reducers.

Sign up to request clarification or add additional context in comments.

18 Comments

Ahh ok, so if I have a list of pokemon upon start of the app (for the user to choose from), then when I first load up the page, are the only actions that have been dispatched the ones to request and receive all pokemon? Or I guess a better question, when I first load up the app, even if I don't click on a pokemon from the initial list, is the "PokemonDetail" component still being loaded?
i can't say without looking at your ParentComponent of "PokemonDetail". even if it loads, now that i have seen your code clearly , if (!pokemon) return null, this should prevent the error. unless your reducer is not updating the state correctly.
Ok, yes, that's what I was thinking with the if (!pokemon) as well...I'll double check the reducer...I'll update the question with my reducer as well
reducer code looks correct. could you log the pokemon data in render method before return and check it ? or show here so that i can also check.
Yes! As an example, when I chose the the fifth pokemon from the list, this is what the pokemon is in the render method when I threw a debugger in there: {id: 5, name: "Charmeleon", image_url: "/assets/pokemon_snaps/5-b47514f3eb8f165ebf18df794acd63f595d5177a32c9233f862741b2d1d95454.svg"} id: 5 name: "Charmeleon" image_url: "/assets/pokemon_snaps/5-b47514f3eb8f165ebf18df794acd63f595d5177a32c9233f862741b2d1d95454.svg" __proto__: Object
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.