JavaScript React Basics Understanding State Remove Items From State

Ceil-Ian Maralit
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Ceil-Ian Maralit
Front End Web Development Techdegree Graduate 19,421 Points

What is wrong with my code?

Hi! Good day. I followed what Guil said in this video but I'm getting an error. I would appreciate a fresh eyes looking at my code. Maybe you will see what's the problem. Thank you in advance!

https://teamtreehouse.com/workspaces/40256612

3 Answers

Henry Lay
Henry Lay
14,739 Points

Hi Ceil-lan,

I can't seem to get into the workspace to provide my input onto the problem if possible are you able to take a screenshot of the question or syntax if possible?

Thank You

Henry

Ceil-Ian Maralit
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Ceil-Ian Maralit
Front End Web Development Techdegree Graduate 19,421 Points

Hi! This is my code:

const Header = (props) => {
    console.log(props)
    return (
        <header>
            <h1>{ props.title }</h1>
            <span className="stats">Players: { props.totalPlayers }</span>
        </header>
    );
}

const Player = (props) => {
    return(
        <div className="player">
            <span className="player-name">
                <button className="remove-player" onClick={ () => this.removePlayer(props.id) }>✖</button>
                { props.playerName }
            </span>

            <Counter />
        </div>  
    );
}

class Counter extends React.Component {

    constructor(){
        super()
        this.state={
            score: 0
        };
    }

    decrementScore() {
        this.setState( prevState => ({
                score: prevState.score - 1
        }));
    }   

    incrementScore() {
        this.setState( prevState => ({
                score: prevState.score + 1
        }));
    }

    render() {
        return(
            <div className="counter">
                <button className="counter-action decrement" onClick={() => this.decrementScore()}> - </button>
                <span className="counter-score">{ this.state.score }</span>
                <button className="counter-action increment" onClick={() => this.incrementScore()}> + </button>
            </div>
        );      
    }
}

class App extends React.Component{
    constructor() {
        super()
        this.state={
            players: [
              {
                name: "Ceil",
                id: 1
              },
              {
                name: "Treasure",
                id: 2
              },
              {
                name: "Ashley",
                id: 3
              },
              {
                name: "James",
                id: 4
              }
            ]   
        };
    }

    handleRemovePlayer(id) {
        this.setState( prevState => {
            return{
                players: prevState.players.filter( p => p.id !== id )
            };
        });
    }

    render() {
        return(
            <div className="scoreboard">
                <Header title="Scoreboard" totalPlayers={this.state.players.length}/>

            {/* Players list */}
            {this.state.players.map( player =>
                <Player 
                playerName={player.name} 
                id={player.id}
                key={player.id.toString()}
                removePlayer={this.handleRemovePlayer}
                />
            )}
            </div>
        );
    }
}

ReactDOM.render(
  <App />, 
  document.getElementById('root')
);
Gabbie Metheny
Gabbie Metheny
33,700 Points

Hi Ceil-Ian,

In the event handler inside your Player component, you're trying to call this.removePlayer, but it actually needs to be props.removePlayer, since the App component passed the function down through props. If you're still getting an error after that change, let me know what the error is and I'd be happy to take another look!

Ceil-Ian Maralit
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Ceil-Ian Maralit
Front End Web Development Techdegree Graduate 19,421 Points

Hi! Thanks for looking, I changed it but I still get the same error, it says two things, tried to figure it out but still can't. This is what it says:

app.js:105 Uncaught TypeError: this.setState is not a function
    at Object.handleRemovePlayer [as removePlayer] (app.js:105)
    at onClick (app.js:31)
    at HTMLUnknownElement.callCallback (react-dom.development.js:139)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:189)
    at invokeGuardedCallback (react-dom.development.js:242)
    at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:256)
    at executeDispatch (react-dom.development.js:587)
    at executeDispatchesInOrder (react-dom.development.js:609)
    at executeDispatchesAndRelease (react-dom.development.js:707)
    at executeDispatchesAndReleaseTopLevel (react-dom.development.js:718)
react-dom.development.js:196 Uncaught Error: A cross-origin error was thrown. React doesn't have access to the actual error object in development. See https://fb.me/react-crossorigin-error for more information.
    at Object.invokeGuardedCallbackDev (react-dom.development.js:196)
    at invokeGuardedCallback (react-dom.development.js:242)
    at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:256)
    at executeDispatch (react-dom.development.js:587)
    at executeDispatchesInOrder (react-dom.development.js:609)
    at executeDispatchesAndRelease (react-dom.development.js:707)
    at executeDispatchesAndReleaseTopLevel (react-dom.development.js:718)
    at forEachAccumulated (react-dom.development.js:688)
    at runEventsInBatch (react-dom.development.js:849)
    at runExtractedEventsInBatch (react-dom.development.js:858)

By the way, it's referring to the handleRemovePlayer, the first error.

Ceil-Ian Maralit
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Ceil-Ian Maralit
Front End Web Development Techdegree Graduate 19,421 Points

Hi Gabbie Metheny , I got it right but it's still wrong. I used the workspace in treehouse and updated my code to this:

const Header = (props) => {
    console.log(props)
    return (
        <header>
            <h1>{ props.title }</h1>
            <span className="stats">Players: { props.totalPlayers }</span>
        </header>
    );
}

const Player = (props) => {
    return(
        <div className="player">
            <span className="player-name">
                <button className="remove-player" onClick={ () => props.removePlayer(props.id) }>✖</button>
                { props.playerName }
            </span>

            <Counter />
        </div>  
    );
}

class Counter extends React.Component {

    constructor(){
        super()
        this.state={
            score: 0
        };
    }

    decrementScore() {
        this.setState( prevState => ({
                score: prevState.score - 1
        }));
    }   

    incrementScore() {
        this.setState( prevState => ({
                score: prevState.score + 1
        }));
    }

    render() {
        return(
            <div className="counter">
                <button className="counter-action decrement" onClick={() => this.decrementScore()}> - </button>
                <span className="counter-score">{ this.state.score }</span>
                <button className="counter-action increment" onClick={() => this.incrementScore()}> + </button>
            </div>
        );      
    }
}

class App extends React.Component{
    constructor() {
        super()
        this.state={
            players: [
              {
                name: "Ceil",
                id: 1
              },
              {
                name: "Treasure",
                id: 2
              },
              {
                name: "Ashley",
                id: 3
              },
              {
                name: "James",
                id: 4
              }
            ]   
        };
    }

    handleRemovePlayer= (id) => {
        this.setState( prevState => {
      return {
                players: prevState.players.filter( p => p.id !== id )
      };
        });
    }

    render() {
        return(
            <div className="scoreboard">
                <Header title="Scoreboard" totalPlayers={this.state.players.length}/>

            {/* Players list */}
            {this.state.players.map( player =>
                <Player 
                playerName={player.name} 
                id={player.id}
                key={player.id.toString()}
                removePlayer={this.handleRemovePlayer}
                />
            )}
            </div>
        );
    }
}

ReactDOM.render(
  <App />, 
  document.getElementById('root')
);

Notice the handleRemovePlayer now.. I changed its format and it worked. BUT, when I try to compile it using babel, I get an error in Babel itself. It has the same function but just with a different format, now the question is.. how is Babel compiler online not accepting this format when the Babel built-in is accepting it and works perfectly fine when using it?

Gabbie Metheny
Gabbie Metheny
33,700 Points

Ah, got it. The line it's pointing you to is the first line of your handleRemovePlayer function, where you use this. In order to have a context for this, handleRemovePlayer needs to be defined as an arrow function. It looks like for incrementScore and decrementScore, you didn't define them as arrow functions, but rather passed them into the click handlers as callbacks using arrow functions. I think that works fine in those instances, since everything exists inside the Counter component, but since here you're passing a function around from parent to child, I'd define it as an arrow function to start so you bind your this right away. It should look something like this:

// current
handleRemovePlayer(id) {
  ...
}

// as arrow function
handleRemovePlayer = (id) => {
  ...
}

I think that change will fix it! You could experiment with other ways of binding this, but I prefer arrow functions personally :)

**EDIT**

Looks like you changed it to an arrow function as I was typing this! Not sure why you're getting a babel issue now, is that using workspaces? I just copied and pasted your latest changes into VSCode, and using the babel cdn Guil provided early on, no errors here.

Ceil-Ian Maralit
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Ceil-Ian Maralit
Front End Web Development Techdegree Graduate 19,421 Points

Thank you guys for going on extra mile on this, Gabbie Metheny and stjarnan . :) I changed its format and it worked. BUT, when I try to compile it using babel, I get an error in Babel itself. It has the same function but just with a different format, now the question is.. how is Babel compiler online not accepting this format when the Babel built-in is accepting it and works perfectly fine when using it? Will that have any issues in real-life when using it? Just curious. Thanks, guys!

Gabbie Metheny
Gabbie Metheny
33,700 Points

Just edited my last comment, take a look at the bottom. Is the babel error happening with workspaces, or your own text editor? Your latest code works perfectly without babel errors for me in VSCode. If the error is happening in workspaces, I'd assume it's a workspaces-specific issue and not worry about it!

Ceil-Ian Maralit
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Ceil-Ian Maralit
Front End Web Development Techdegree Graduate 19,421 Points

Yeah, it's happening in my text editor. But, the issue now is yeah.. it's working on treehouse workspaces but when I copy the right code and paste it on the babel website and try to compile it, it throws me an error "Unexpected token" pointing to the handleRemovePlayer. I'm just wondering why is it not accepting it that syntax?

stjarnan
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
stjarnan
Front End Web Development Techdegree Graduate 56,487 Points

Hi Ceil-Ian Maralit,

I noticed you tagged me and took a quick look, I believe the answer Gabbie Metheny came up with just as you tagged me might solve your problem. (I can see that you tagged me 20 seconds before her reply came in! :D )

Let us know how it works!

stjarnan
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
stjarnan
Front End Web Development Techdegree Graduate 56,487 Points

I also tried running your code using VSCode and like Gabby, I had no errors. It might be a version problem you're experiencing. Are you using the same Babel version that Guil is using?

Ceil-Ian Maralit
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Ceil-Ian Maralit
Front End Web Development Techdegree Graduate 19,421 Points

I guess so cause I went to its website and pasted the right code there, but it's throwing an "Unexpected token" error, pointing to the handleRemovePlayer.

Gabbie Metheny
Gabbie Metheny
33,700 Points

Is this using the Babel REPL Guil posted in one of the first videos? If I try to evaluate your code there, I get some errors, too, but I think it's just because my environment isn't set up properly for React, etc. As long as your code works locally, it's probably fine, though you'd need to compile it properly for production. The next React course is supposed to launch this Wednesday, and I believe it uses create-react-app, which takes care of babel and all the other build stuff for you, since it gets pretty messy otherwise ;)

stjarnan
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
stjarnan
Front End Web Development Techdegree Graduate 56,487 Points

I can confirm what Gabbie is saying, the Babel website editor will give you a couple of errors as it does not understand the contexy of the code. Don't worry about that :)