Search code examples
arraysreactjsreact-nativemappingincrement

React Native - Unable to update a new state inside items individually


I have pulled data from an api which has a list of exercises inside, here is an example exericse object.

    {
        pivot_id:1,
        id:1,
        image_path: "exercise\/images\/q3YaO7uuFYgZDFg7fnVpn3eYxrb5oUZeKwE6mNf7.png",
        name:"Smitham PLC",
        duration:423,
        duration_per_rep:5,
        intensity:{
          id:2,
          intensity: "Moderate Resistance Training",
          met_value:4.5
        },
        image:"https:\/\/s3-eu-west-1.amazonaws.com\/fitness-concierge\/exercise\/images\/q3YaO7uuFYgZDFg7fnVpn3eYxrb5oUZeKwE6mNf7.png",
        kcal:5,
  },

I have created a customDuration state with a default value of 60, which I want to add to each exercise object by using a button to add a duration with an increment of 30 seconds. However, I am having an issue with updating just one specific item.

For example, I have two items rendered, and when I go to press the + button to add 30 seconds to the exercise it does, it goes from 60 - 90, but then when I go and add some time on the second item in the list it will go from 60 - 120 which is were the problem is occurring.

 addDuration(item, index) {
    let newArray = [...this.state.warm_up_exercises];
    newArray[index] = { 
        ...newArray[index], 
        customDuration: this.state.customDuration = this.state.customDuration + 30,
        storeValues: true,
        workoutStoreType: 'duration',
    };
    this.setState({
        warm_up_exercises: newArray,
    });
}

Above is the function I am using to add a duration to the exercise, I have tried to do item.customDuration = item.customDuration + 30 but I just get NaN in the console.

In my mapping function I have the custom duration showing like this

<Text>{{ item.customDuration }}</Text>

I've been struggling with this a couple of days now so much help would be really appreciated. All I am trying to achieve is to just make it so when I add the values to the array, it will update that item and have no effect on the other items.

Additional Code to provide more context:

My constructor:

constructor(props) {
  super(props) {
    warm_up_exercises: [],
    selected_warm_up_exercises: [],
    repsValue: 0,
    setsValue: 0,
    restValue: 0,
    workoutStoreType: "",
    customDuration: 60,
  }
}
Were I call the function: 

<TouchableOpacity onPress={() => this.addDuration(item, index)}>
   <AntDesign name="pluscircle" size={22} color={Colours.type.primary_colour}  />
</TouchableOpacity>

Solution

  • First, you are not properly initialising your state. It should be an object, which has the exercise array as a property. Write it like this:

    constructor(props) {
      super(props) {
        this.state = {
         exercises: [],
         repsValue: 0,
         setsValue: 0,
         restValue: 0,
         workoutStoreType: "",
         customDuration: 60,
        }
      }
    }
    

    Every time you increment the state you are not just mutating it, but you also put customDuration to every entity. The problem lies here:

    customDuration: this.state.customDuration = this.state.customDuration + 30
    

    In order to do it right, you just need to write:

    customDuration: this.state.customDuration + 30
    

    And then, to increment the state you should do it separately :

    this.setState({
            exercises: newArray,
            customDuration: this.state.customDuration + 30
        });
    

    In case you need to have a customDuration to every item you also need to change the state in the same way, but with a bit of a twist:

    addDuration(item, index) {
        let newArray = [...this.state.exercises]
        newArray[index] = {
            ...newArray[index],
            customDuration: newArray[index].customDuration ? newArray[index].customDuration + 30 : 90,
            storeValues: true,
            workoutStoreType: 'duration',
        };
        this.setState({
            exercises: newArray,
        });
    }
    

    And then remove customDuration from the initial state. This way you will increment by 30 and put 90 the first time you increment