Search code examples
reactjsreact-nativeasynchronousstateasyncstorage

Why is React Native AsyncStorage not updating state on mount?


When I try to load the state from AsyncStorage for the screen I just navigated to, I am getting this error: TypeError: undefined is not an object (evaluating 'weights[numExercise].map') It is trying to use the initial state that the screen initializes the state with, but I want the state to be loaded with the data that I specifically try to load it with on mount, within my useEffect hook.

const WorkoutScreen = ({ navigation, route }) => {
  const [workoutName, setWorkoutName] = useState("");
  const [exercisesArr, setExercisesArr] = useState([""]);
  // Each array inside the arrays (weights & reps), represents an exercise's sets.
  const [weights, setWeights] = useState([[""]]);
  const [reps, setReps] = useState([[""]]);
  const [restTimers, setRestTimers] = useState([""]);

useEffect(() => {
    try {
      console.log("loading workoutscreen data for:", route.params.name);
      const unparsedWorkoutData = await AsyncStorage.getItem(route.params.name);

      if (unparsedWorkoutData !== null) {
        // We have data!
        const workoutData = JSON.parse(unparsedWorkoutData);

        setWorkoutName(route.params.name.toString());
        setExercisesArr(workoutData[0]);
        setWeights(workoutData[1]);
        setReps(workoutData[2]);
        setRestTimers(workoutData[3]);
      }
    } catch (error) {
      // Error retrieving data
      console.log("ERROR LOADING DATA:", error);
    }
  }, []);

Then further down the line in a component it realizes the error because, again, it's using the initialized state for the weights state.

  Return (
     {weights[numExercise].map((weight, i) => {
        return (
          <SetComponent
            key={i}
            numSet={i}
            numExercise={numExercise}
            prevWeights={prevWeights}
            weights={weights}
            setWeights={setWeights}
            prevReps={prevReps}
            reps={reps}
            setReps={setReps}
            isDoneArr={isDoneArr}
            setIsDoneArr={setIsDoneArr}
          />
        );
      })}
  );

I've made sure that the data is being stored, loaded, and used correctly, so (I think) I've narrowed it down to be something asynchronous; whether it's the setting of the state or loading from storage, I don't know and I can't find a solution. I am new to React Native and would love some suggestions, thank you!


Solution

  • It turns out that using multiple states was causing an issue, I'm assuming because it's asynchronous. So instead I used one state that held an object of states, like so:

    const [states, setStates] = useState({
        workoutName: "",
        exercisesArr: [""],
        weights: [[""]],
        reps: [[""]],
        restTimers: [""],
        isDoneArr: [[false]],
        originalWorkoutName: "",
      });
    

    The data was loaded as such:

      const loadWorkoutData = async () => {
        try {
          console.log("loading workoutscreen data for:", route.params.name);
          const unparsedWorkoutData = await AsyncStorage.getItem(route.params.name);
    
          if (unparsedWorkoutData !== null) {
            // We have data!
            const workoutData = JSON.parse(unparsedWorkoutData);
    
            setStates({
              workoutName: route.params.name,
              exercisesArr: workoutData[0],
              weights: workoutData[1],
              reps: workoutData[2],
              restTimers: workoutData[3],
              isDoneArr: workoutData[4],
              originalWorkoutName: route.params.name,
            });
          }
        } catch (error) {
          // Error retrieving data
          console.log("ERROR LOADING DATA:", error);
        }
      };