Valchy
Valchy

Reputation: 315

Rerender Component upon state change in React

I have an app which calls two functions; MyCart and MyItems. I am trying to make it so when the button rendered in MyItems is called it calls a method inside the main app component (the parent) and changes the state but for some reason, it doesn't change the value in the MyCart span.

My code below is what I am trying to do but it's not working and I can't figure out why.

class ShopRemake extends React.Component {
    constructor() {
        super();
        this.state = {
            total: 0
        }

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

    changeTotal() {
        this.setState(prevState => {total: ++prevState.total});
        console.log(this.state);
    }

    render() {
        return (
            <React.Fragment>
                <MyItem changeTotal={this.changeTotal}/>
                <MyCart total={this.state.total}/>
            </React.Fragment>
        );
    }
}

function MyItem(props) {
    return (
        <button onClick={props.changeTotal}> Click Me </button>
    );
}

function MyCart(props) {
    return (
        <span> Total Items: {props.total} </span>
    );
}

I want it so that after I click the button the span inside the MyCart to update accordingly.

Upvotes: 1

Views: 77

Answers (2)

Treycos
Treycos

Reputation: 7492

You must wrap the returning object literal into parentheses in your arrow functions. Otherwise curly braces will be considered to denote the function’s body. The following works:

p => ({ foo: 'bar' })

So the call to setState in changeTotal :

changeTotal() {
    this.setState(prevState => {total: ++prevState.total});
    console.log(this.state);
}

Should be replaced by the following :

changeTotal() {
    this.setState(prevState => ({total: ++prevState.total}));
    console.log(this.state);
}

Also, setState is asynchronous, you will need to either await it(not recommended) or use the callback as the second parameter to see the resulting state :

changeTotal() {
    this.setState(
        prevState => ({total: ++prevState.total}),
        () => { console.log(this.state) }
    );
}

EDIT :

Sidenote, the 2 components below in your code can be reduced into arrow functions for the exact same result, while making them easier to read :

const MyItem = ({ changeTotal }) => <button onClick={changeTotal}> Click Me </button>

const MyCart = ({ total }) => <span> Total Items: {total} </span>

And you can also convert changeTotal to an arrow function to avoid having to bind it in your constructor

Upvotes: 2

tolotra
tolotra

Reputation: 3270

Your setState is not returning an object here. Try to change your method changeTotal to this

changeTotal() {
     this.setState(prevState => ({ total: prevState.total + 1 }), () => 
     console.log(this.state));
}

The setState methods is asynchronous, if you want to log it's value, do this with a callback.

Upvotes: 2

Related Questions