Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use `setState` with objects nested in an array in React JS?

With this code, I am able to successfully use setState on a simple object – when I click on "Joey" the name changes to "Igor".

    class Card extends React.Component {
        myFunc = () => {this.props.change('Igor')};
        render() {
            return (
                <p onClick={this.myFunc}>{this.props.name}</p>
            )
        }
    }

    class Parent extends React.Component {
        constructor(props) {
            super(props)
            this.state = { name: "Joey" }
        }

        toggle = (newname) => {
            this.setState((prevState, props) => ({
                name: newname
            }));
        }

        render() {
            return (
                <Card change={this.toggle} name={this.state.name} />
            );
        }
    }

But with this code, which has multiple objects nested in an array, setState is either not able to change each name to "Igor" or it must be modified in some way.

    class Card extends React.Component {

        myFunc = () => {this.props.change('Igor')};
        render() {
            return (
                <p onClick={this.myFunc}>{this.props.name}</p>
            )
        }
    }

    class Parent extends React.Component {
        constructor(props) {
            super(props)
            this.state = {
                names: [
                    {
                        name: "Joey"
                    },
                    {
                        name: "Sally"
                    },
                    {
                        name: "Billy"
                    },
                ]
            }
        }

        toggle = (newname) => {
            this.setState((prevState, props) => ({
            // what can I put here to change the name I click on to "Igor"
            }));
        }

        render() {
            const names = this.state.names.map((name, index) => (
                <Card key={index} change={this.toggle} {...name} />
            ))

            return (
                <div>
                    {names}
                </div>
            );
        }
    }

Even though I know this is not how setState works, I tried to access name by passing index and then writing this.state.names[index].name: newname. No surprises here, it didn't work.

I have researched and cannot find similar questions on SO about this although I have found a lot of mentions with regards to immutability helpers. But I am still not sure if that is the way to go.

What is the best way to use setState to modify objects nested in an array?

like image 703
samurai_jane Avatar asked Oct 30 '22 08:10

samurai_jane


2 Answers

Have modified your code and the working example can be found here.

The changes can be found here:

toggle = (index, newname) => {
    this.setState((prevState, props) => ({
        // Return new array, do not mutate previous state.
        names: [
            ...prevState.names.slice(0, index),
            { name: newname },
            ...prevState.names.slice(index + 1),
        ],
    }));
}

render() {
    const names = this.state.names.map((name, index) => (
        // Need to bind the index so callback knows which item needs to be changed.
        <Card key={index} change={this.toggle.bind(this, index)} {...name} />
    ))

    return (
        <div>
            {names}
        </div>
    );
}

The idea is that you need to pass the index into the callback function via .bind, and return a new state array with the modified name. You need to pass the index so that the component knows which object to change the name to newname.

like image 70
Yangshun Tay Avatar answered Nov 15 '22 06:11

Yangshun Tay


I would use this for the toggle method:

toggle = (nameYouWantChanged, nameYouWantItChangedTo) => {

  this.setState({
    names: this.state.names.map(obj => 
      obj.name === nameYouWantChanged
        ? { name: nameYouWantItChangedTo }
        : obj
    )
  })

}
like image 43
Brad Woods Avatar answered Nov 15 '22 04:11

Brad Woods