react-nativereact-hooksasyncstorage

Adding new object to useState array


So I am trying to add to a useState array of objects however it is getting overwritten everytime I try to add something new to it.

I define my state

const [habits, setHabits] = useState([]);

make multiple calls to return a data object

asyncStorageKeys.forEach(key => {
 getDataObject(key, habits, setHabits);
});

getDataObject looks like this

export const getDataObject = async (key, data, setData) => {
  try {
    const jsonValue = await AsyncStorage.getItem(key);
    return jsonValue != null ? setData([...data, JSON.parse(jsonValue)]) : null;
  } catch (e) {
    console.error('Error returning data object in AsyncStorage, error: ' + e);
  }
};

But when I console.log using

useEffect(() => {
  console.log(habits);
}, [habits]);

It shows that the array is just being overwritten everytime

 LOG  []
 LOG  [{"daysPerWeek": 3, "name": "another one"}]
 LOG  [{"daysPerWeek": 7, "name": "test"}]
 LOG  [{"daysPerWeek": 6, "name": "testing"}]
 LOG  [{"daysPerWeek": 6, "name": "tests"}]

I have looked on react.dev (https://react.dev/learn/updating-arrays-in-state) for the recommended way to do this however it is not working for me.


Solution

  • Whenever you call setData([...data, JSON.parse(jsonValue)]) you add the current item to the original state and then set it as the state, and the last addition would overwrite all the others. For example, if you have a state of [1, 2, 3], and you want to add the values - 4, 5, 6, the end state would be [1, 2, 3, 6]. See Updating state based on the previous state.

    To prevent that you can you can use an updater function, that takes the last updated state on each iteration:

     setData(prev => [...prev, JSON.parse(jsonValue)])
    

    However, it's better make all async calls at once using Array.map(), wait for all of them to finish with Promise.all(), and then update the state while filtering null values:

    export const getDataObjects = async() => {
      try {
        const values = await Promise.all( // wait for all promises to resolve
          // create an array of promises
          asyncStorageKeys.map(key => 
            AsyncStorage.getItem(key).then(v => JSON.parse(v))
          )
        );
        
        // add all non null values to previous state
        setData(prev => [...prev, ...values.filter(v => v !== null)])
      } catch (e) {
        console.error('Error returning data object in AsyncStorage, error: ' + e);
      }
    };