Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

JavaScript Building Applications with React and Redux Putting it all Together Updating the Player, Counter and AddPlayerForm Components

Jonathan Grieve
MOD
Jonathan Grieve
Treehouse Moderator 90,705 Points

cannot read property map undefined

Hello all,

So there's another error with rendering the app but the Git Shell is compiling. The error in the console is

Uncaught TypeError: Cannot read property 'map' of undefined
    at Scoreboard.render (bundle.js:33508)
    at bundle.js:19806
    at measureLifeCyclePerf (bundle.js:19086)
    at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (bundle.js:19805)
    at ReactCompositeComponentWrapper._renderValidatedComponent (bundle.js:19832)
    at ReactCompositeComponentWrapper.performInitialMount (bundle.js:19372)
    at ReactCompositeComponentWrapper.mountComponent (bundle.js:19268)
    at Object.mountComponent (bundle.js:17591)
    at ReactCompositeComponentWrapper.performInitialMount (bundle.js:19381)
    at ReactCompositeComponentWrapper.mountComponent (bundle.js:19268)

Again, Would really appreciate a pointer or 2 on this if possible. Thanks :)

Scorebard.js
import React, {Component, PropTypes} from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as PlayerActionCreators from '../actions/player';
import AddPlayerForm from '../components/AddPlayerForm';
import Player from '../components/Player';
import Header from '../components/Header';


class Scoreboard extends Component {

    static PropTypes = {
        players: PropTypes.array.isRequired
    };

    render() {

        const { dispatch, players } = this.props;
        const addPlayer = bindActionCreators(PlayerActionCreators.addPlayer, dispatch);
        const removePlayer = bindActionCreators(PlayerActionCreators.removePlayer, dispatch)
        const updatePlayerScore = bindActionCreators(PlayerActionCreators.updatePlayerScore, dispatch) ;


        const playerComponents = players.map((player, index) => (
            <Player
                index={index}
                name={player.name}
                score={player.score}
                key={player.name}
                updatePlayerScore={updatePlayerScore}
                removePlayer={removePlayer}
             />

        ));

        return (
          <div className="scoreboard">
            <Header players ={ players } />
            <div className="players">
                { playerComponents }
            </div>
            <AddPlayerForm onAdd={addPlayer} />
          </div>
        );
      }
}

const mapStateToProps = state => (
    {
        player: state

    }
);

export default connect(mapStateToProps)(Scoreboard);

Updated Repo https://bitbucket.org/jg_digitalMedia/react-redux

5 Answers

Thomas Fogg
Thomas Fogg
2,723 Points

I came across the same problem as you. The problem isn't in your scoreboard.js but your player.js reducer. This is how Guil has you write the file:

import * as PlayerActionTypes from '../actiontypes/player'

const initialState = {
  players: [
    {
      name: 'Jim Hoskins',
      score: 31,
    },
    {
      name: 'Andrew Chalkley',
      score: 20,
    },
    {
      name: 'Alena Holligan',
      score: 50,
    },
  ]
}

export default function Player(state=initialState, action) {
  switch(action.type) {
    case PlayerActionTypes.ADD_PLAYER:
    return [
      ...state,
      {
        name: action.name,
        score: 0
      }
    ]

    case PlayerActionTypes.REMOVE_PLAYER:
      return [
        ...state.slice(0, action.index),
        ...state.slice(action.index + 1)
      ]

    case PlayerActionTypes.UPDATE_PLAYER_SCORE:
      return state.map((player, index) => {
        if(index === action.index) {
          return {
            ...player,
            score: player.score + action.score
          }
        }
        return player
      })
  }
}

But this is how it actually needs to look:

import * as PlayerActionTypes from '../actiontypes/player';

const initialState = {
    players: [{
        name: 'Jim Hoskins',
      score: 31,
    },
    {
        name: 'Andrew Chalkley',
        score: 20,
    },
    {
        name: 'Alena Holligan',
        score: 50,
    }
    ]
}

export default function Player(state=initialState, action) {

  switch(action.type){
    case PlayerActionTypes.ADD_PLAYER: {
            const addPlayerList = [...state.players,   {
          name: action.name,
          score: 0
      }];
      return {
        ...state,
                players: addPlayerList
            };
        }

    case PlayerActionTypes.REMOVE_PLAYER: {
            const removePlayerList = [
                ...state.players.slice(0, action.index),
                ...state.players.slice(action.index + 1)
            ];
      return {
                ...state,
                players: removePlayerList
            };
        }

    case PlayerActionTypes.UPDATE_PLAYER_SCORE: {
            const updatePlayerList = state.players.map((player, index) => {
        if(index === action.index){
          return {
            ...player,
             score: player.score + action.score
           };
        }
        return player;
      });
            return {
                ...state,
                players: updatePlayerList
            };
        }
    default:
      return state;
  }
}

The indentation is skewed here but notice the constants under each of the PlayerActionType blocks. Guil goes over this change later in the course but as far as I can tell the app won't function without the change. Hopefully this helps anyone else struggling with this problem in the future.

Jonathan Grieve
Jonathan Grieve
Treehouse Moderator 90,705 Points

Thanks for this, Thomas.

I had parked this project for all this time but I'd tried your edits and after a bit more debugging the project is now compiling!!!

As a result of your suggested edits I made a few more changes to the playerComponents map in scoreboard.js

const playerComponents = players.map((players, index) => (
                <Player
                    index={index}
                    name={players.name}
                    score={players.score}
                    key={players.name}
                    updatePlayerScore={updatePlayerScore}
                    removePlayer={removePlayer}
                />
            ));

All sorted! :)

Guil Hernandez
STAFF
Guil Hernandez
Treehouse Teacher

Hey Jonathan Grieve,

In mapStateToProps, player: state should be players: state.players. Hope this helps!

Jonathan Grieve
Jonathan Grieve
Treehouse Moderator 90,705 Points

Hi Guil,

Thanks for this, I did make the change but sadly no effect

I had the exact same problem. The state was returned in the store but did not show up as props in the Scoreboard component. For me, the problem was with my action -> player.js.

Make sure all the methods in player js must match all the bindActionCreator methods. Hope this helps

Jonathan Grieve
Jonathan Grieve
Treehouse Moderator 90,705 Points

Hi Michael, thanks for replying! This is still an open issue for me so I'll have a try with your advice and let you know how it goes! :-)

Jonathan Grieve
Jonathan Grieve
Treehouse Moderator 90,705 Points

Thanks both for your tries. Sadly this bug isn't going to go away.

Chrome DevTools seems to think though that the problem is in line 23 of scoreboard.js

 const playerComponents = player.map((player, index) => (
Micah Dunson
Micah Dunson
34,368 Points

I had a similar issue. Maybe check to make sure you are returning the player object in your reducer switch function. I didn't catch it at first in the video.

case PlayerActionTypes.UPDATE_PLAYER_SCORE: return state.map((player, index) => { if (index === action.index) { return { ...player, score: player.score + action.score }; } === return player; // I had previously forgot this line ======== });

It looks like you're implementing destructuring assignment in this line of code:

 const { dispatch, players } = this.props;

which states that you want to unpack a "players" property from an object defined in mapStateToProps. However, in mapStateToProps at the bottom of scoreboard.js, you set property to "player" and not "players". Therefore, the "players" prop is undefined, and invoking .map on "players" will return the error you have. That's my guess. Hope this helps.

For more info on destructuring assignment: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment