ReactJS - setState of Object key in Array

52,503

Solution 1

Get current state, modify it and setState() it:

var stateCopy = Object.assign({}, this.state);
stateCopy.items[key].upVotes += 1;
this.setState(stateCopy);

Note: This will mutate the state. Here's how to do it without mutation:

var stateCopy = Object.assign({}, this.state);
stateCopy.items = stateCopy.items.slice();
stateCopy.items[key] = Object.assign({}, stateCopy.items[key]);
stateCopy.items[key].upVotes += 1;
this.setState(stateCopy);

Solution 2

It's possible to directly edit the value on your array and set the state to the modified object, considering you're not using immutable.js, that is...

this.state.array[i].prop = 'newValue';
this.setState({ array: this.state.array });

The problem with direct editing is that React doesn't know the state changed and the update lifecycle doesn't fire. But setting the state again forces an update.

-- EDIT --

If state is Immutable...

const array = this.state.array.slice();
array[i].prop = 'newValue';
this.setState({ array });

-- EDIT 2 --

Thanks to the selected answer I realized this would still mutate the element since the array contains only references to the object in question. Here's a concise ES6-y way to do it.

const array = [...this.state.array];
array[i] = { ...array[i], prop: 'New Value' };
this.setState({ array });
Share:
52,503
Author by

Elliott McNary

Updated on May 27, 2020

Comments

  • Elliott McNary over 2 years

    So i've been working on this for awhile and felt it would be best to refactor my code so that the state is set up as an array of objects. What i'm trying to do is increment a number on a button click.

    I have a callback function in a component that triggers a function to update the state...however i'm having difficulty targeting the key value within the object.

    My initial state looks like this:

    getInitialState: function() {
        return {
          items: [
            {
              links: 'zest',
              trackId: 1023,
              songTitle: 'z know the others',
              artist: 'zuvet',
              upVotes: 0
            },
            {
              links: 'alpha',
              trackId: 987,
              songTitle: 'ass',
              artist: 'arme',
              upVotes: 3
            },
          ]
        }
    

    I am trying to target the upVotes key, but can't figure out how. My function passes a key so that I can target the index in the array, but when I try to do something like: this.setState({items[key]: {upVotes: this.state.items[key].upVotes + 1}}) it throws an error due to the unexpected [ token.

    I have tried something similar to this thread here, but I keep getting errors.

    What kind of function can I write that will setState of just the key in the object that I want to target?

  • crak
    crak over 5 years
    after the react documentation, everything should be donne in setState( oldState => your code )
  • Radosław Miernik
    Radosław Miernik over 5 years
    @crak: checkout the setState API.
  • Raúl Salinas-Monteagudo
    Raúl Salinas-Monteagudo over 5 years
    I do not know if it is for a good reason, but it it often discouraged to wildly directly mutate the state outside of a this.setState() call.
  • The Walrus over 4 years
    @RadosławMiernik where is key coming from??
  • Radosław Miernik
    Radosław Miernik over 4 years
    @TheWalrus it's mentioned in the question, therefore I assumed it is defined somewhere.
  • Vinícius Negrão over 4 years
    the snippet is only part of the code, so I assumed i would be defined since it's the element to be mutated
  • Павел Зорин almost 3 years
    What is it for? stateCopy.items = stateCopy.items.slice();
  • Radosław Miernik
    Radosław Miernik over 2 years
    @ПавелЗорин: setting an array index to a given value (array[index] = value) is modifying the array. We cannot alter it, so we'd have to (shallow) copy the array - slice is a great way of doing so.
  • Ryan almost 2 years
    Your second solution still mutates the state directly. Array.prototype.slice() only creates a shallow copy of the array, so each element of stateCopy.items points to the same object as the elements of this.state.items
  • Radosław Miernik
    Radosław Miernik almost 2 years
    Yes, .slice() creates a shallow copy. That's why there's this Object.assign in the third line, cloning the array element.