alvirbismonte
alvirbismonte

Reputation: 397

Correct way to setState a property of nested object with array properties

Good day! I have a nested state with array children, and I would like to know the correct and best way to setState.

Here's a sample state object. EDITED, Forgot to mention that lists is an array of objects.

this.state = {
     lists:
          [{

               name: 'sampleList',
               id: 15,
               permission: {
                    canRead: true,
                    canWrite: false
               }
          }]
     }
}

I use this to setState of the permission's properties but the state is not updated.

this.setState({
     ...this.state, lists: {
          ...this.state.lists, key: [
               ...this.state.lists.key, permission : {
                    ...this.state.lists.key.permission, 
                    canRead: !this.state.lists.key.permission.canRead
               }
          ] 
     }
 });

I hope you can help me. Thanks!

Upvotes: 2

Views: 69

Answers (2)

ilonacodes
ilonacodes

Reputation: 179

The way to change a specific element of the array with setState is to copy all items of array as they are without changing, except for the target item, this can be done with .map and if statement:

export default class App extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            lists: [
                {
                    name: 'sampleList',
                    id: 15,
                    permission: {
                        canRead: true,
                        canWrite: false
                    }
                }
            ]
        }
    }

    invertCanRead(targetId) {
        this.setState({
            ...this.state,
            lists: this.state.lists.map(list => {
                if (list.id === targetId) {
                    // that is the one we want to modify, so let's return
                    // modified version:
                    return {
                        ...list,
                        permission: {
                            ...list.permission,
                            canRead: !list.permission.canRead
                        }
                    }
                } else {
                    // else return list as is without changes because
                    // it's not the one we want to modify, so just copy it:
                    return list;
                }
            })
        })
    }

    render() {
        return <div>
            {JSON.stringify(this.state)};
            <button onClick={() => this.invertCanRead(15)}></button>
        </div>
    }
}

Upvotes: 1

Tholle
Tholle

Reputation: 112927

You can create a copy of the lists array, and then replace the object of the index you want to change with a copy of that same object but where you change the permission.

Example

class App extends React.Component {
  state = {
    lists: [
      {
        name: "sampleList",
        id: 15,
        permission: {
          canRead: true,
          canWrite: false
        }
      }
    ]
  };

  updateList = (index, canRead, canWrite) => {
    this.setState(prevState => {
      const lists = [...prevState.lists];
      lists[index] = { ...lists[index], permission: { canRead, canWrite } };
      return { lists };
    });
  };

  render() {
    return (
      <div>
        <button onClick={() => this.updateList(0, false, true)}>
          Update state
        </button>
        <div>{JSON.stringify(this.state)}</div>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="root"></div>

Upvotes: 2

Related Questions