'My react-native's increment counter using useState suddenly stopped incrementing. None of the existing solutions I found worked

My app has a component that consists of three buttons(only two are relevant though.) Yesterday I discovered that my setanimalNumber(animalNumber + 1) has stopped working. Animal number no longer increments.

const AnimalButton = ({remove, animalz, addtoList}) => {
    const [animalNumber, setanimalNumber] = useState(1)  
    return (
        <View style={styles.buttonGrouptrue} >
            <Button title={animalNumber + ' ' + animalz}
                onPress={() => remove(animalz)} 
                buttonStyle={styles.button} titleStyle={{color: 'black'}}
            />
            <View style={styles.directionalButtons}>
                          
                <TouchableOpacity
                    onPress={() => {
                    setanimalNumber(animalNumber, animalNumber + 1)
                    addtoList([animalz, animalNumber + 1])      
                    }}
                    style={styles.arrowButton} 
                    >
                    <Image
                        style={{  
                            marginLeft: 'auto',
                            marginRight: 'auto'
                        }}
                        source={require('../assets/arrow_up.png')}
                    ></Image>
                </TouchableOpacity>          
            </View>
        </View>
    );
}

I have looked around throughout stack overflow and I have tried the following options.

setanimalNumber(animalNumber => animalNumber + 1)
setanimalNumber(animalNumber++)
setanimalNumber(animalnNumber, animalNumber + 1)

const test = (value) => {
let newValue = value + 1
setanimalNumber(newValue)
}

I know this is a common issue, but none of the solutions I found seemed to work. Any advice at all would be appreciated.



Solution 1:[1]

Issue

Of the following implementations, two are correct, and two are incorrect.

Incorrect

  • setAnimalNumber(animalNumber, animalNumber + 1)

    The useState state updater function takes only a single argument, either the value to update the state to or a callback function. This implementation only sets the state to the current value and ignores the second argument.

  • setAnimalNumber(animalNumber++)

    This version will technically update the state value, but it mutated the state and won't trigger a rerender to see the new value.

Correct

  • const test = (value) => {
      let newValue = value + 1;
      setAnimalNumber(newValue);
    }
    

    This version will update the state, but doesn't take into account multiple enqueued state updates.

    Example:

    const [animalNumber, setAnimalNumber] = useState(1);
    
    ...
    
    test(animalNumber); // 1 + 1 -> 2
    test(animalNumber); // 1 + 1 -> 2
    test(animalNumber); // 1 + 1 -> 2
    

    The result of invoking test(animalNumber); three times is that only was added to theanimalNumber` state.

  • setAnimalNumber(animalNumber => animalNumber + 1)

    This is the correct way to enqueue a state update that depends on the value of the previous state.

    setAnimalNumber(animalNumber => animalNumber + 1); // 1 + 1 -> 2
    setAnimalNumber(animalNumber => animalNumber + 1); // 2 + 1 -> 3
    setAnimalNumber(animalNumber => animalNumber + 1); // 3 + 1 -> 4
    

    The result of invoking setAnimalNumber(animalNumber => animalNumber + 1); three times is that the previous state's value is used for each update.

    See Functional Updates.

Solution

const AnimalButton = ({ remove, animalz, addtoList }) => {
  const [animalNumber, setAnimalNumber] = useState(1);

  return (
    <View style={styles.buttonGrouptrue}>
      <Button
        title={animalNumber + ' ' + animalz}
        onPress={() => remove(animalz)}
        buttonStyle={styles.button}
        titleStyle={{ color: 'black' }}
      />
      <View style={styles.directionalButtons}>
        <TouchableOpacity
          onPress={() => {
            setAnimalNumber(number => number + 1); // <-- functional state update
            addtoList([animalz, animalNumber + 1]);
          }}
          style={styles.arrowButton}
        >
          <Image
            style={{
              marginLeft: 'auto',
              marginRight: 'auto'
            }}
            source={require('../assets/arrow_up.png')}
          />
        </TouchableOpacity>
      </View>
    </View>
  );
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Drew Reese