5

I am using React, my state is defined as an array of object. I need to be able to change only one specific element in the state.data array, example object with id 1.

I would like to know:

  • what is the proper way how to use setState() in this scenario.

constructor(props) {
  super(props);
  this.state = {
    data: [{
        id: 0,
        title: 'Buy a',
        status: 0, // 0 = todo, 1 = done
      },
      {
        id: 1,
        title: 'Buy b',
        status: 0,
      },
      {
        id: 2,
        title: 'Buy c',
        status: 0,
      }
    ]
  };
  this.onTitleChange = this.onTitleChange.bind(this);
}
onTitleChange(id, title) {
  console.log(id, title);
  debugger
}

5 Answers 5

17

You can get do a cloning of the state object using spread operator and then find the index of object in array with a given id using findIndex method Modify the object and set the state.

constructor(props) {
  super(props);
  this.state = {
    data: [{
        id: 0,
        title: 'Buy a',
        status: 0, // 0 = todo, 1 = done
      },
      {
        id: 1,
        title: 'Buy b',
        status: 0,
      },
      {
        id: 2,
        title: 'Buy c',
        status: 0,
      }
    ]
  };
  this.onTitleChange = this.onTitleChange.bind(this);
}
onTitleChange(id, title) {
   var data = [...this.state.data];
   var index = data.findIndex(obj => obj.id === id);
   data[index].title = title;
   this.setState({data});
}
Sign up to request clarification or add additional context in comments.

1 Comment

Good answer. Just want to point out to @Radex that setState should be used with caution: the onTitleChange function should not be called into a lifecycle method where the component is updating (eg. componentDidUpdate) because otherwise it would cause an infinite loop. Check this doc entry facebook.github.io/react/docs/state-and-lifecycle.html beforehand
1

You can also modify the way you are

storing the state in the below format

for ease, hope this helps!

constructor(props) {
  super(props);
  this.state = {
    data: [
     0: {
        id: 0,
        title: 'Buy a',
        status: 0, // 0 = todo, 1 = done
      },
     1: {
        id: 1,
        title: 'Buy b',
        status: 0,
      },
     2: {
        id: 2,
        title: 'Buy c',
        status: 0,
      }
    ]
  };
  this.onTitleChange = this.onTitleChange.bind(this);
}

onTitleChange(id, title) {
   var newData = [...this.state.data];
   newData[id].title = title;
   this.setState({newData});
}

1 Comment

I think, it should be an index in your newData[index].title = title;
1

You could also do something like this:

onChange = (id, value, field) => { 
    this.setState((prevState) => ({
            data: prevState.data.map((d, index) => { //d = object, index = index in array
                if (d.id === id) {
                    return {
                        ...d,
                        [field]: value //field/name in object
                    }
                }
                return d
            })
        }), () => {
            console.log("New value of", field, "=", value, "in object with id", id);
        });
}

Comments

0

A simple solution would be:

const idx = this.state.data.findIndex(obj => obj === id);
this.state.data[idx].title = title;

For more complex component, I would recommend to use Immutable.js List

2 Comments

I need to use setState, otherwise the component would render property when updated
is this considered as mutating the state?
0

I would use the spread operator to update the state.

onTitleChange(id, title) {
  const { data } = this.state
  const index = data.findIndex(d => d.id === id)

  this.setState({
    data: [
      ...data.slice(0, index),
      {
        ...data[index],
        title: title,
      },
      ...data.slice(index + 1)
    ]
  })
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.