kobo
kobo

Reputation: 31

How do I access the data from a referenced firestore document?

My data in firestore is structured as shown in the pictures. In my code, I am using a map of the exercises array within my workout document like this:

{workout.exercises.map((exercise) => {
    return (
        <Text>Want exercise name here</Text>
    )

I believe I have to use doc.data() from getDoc(exercise).then((doc) => to get the actual values, but I can't seem to figure out how to actually render the exercise name. It's driving me crazy, any help would be greatly appreciated!

enter image description here

This is the exercise object referenced in the exercises array from the picture above:

enter image description here

EDIT: If the exercises array only has one element, it works properly, but if it has >1, the data seems to keep rendering infinitely which results in flashing and changing text.

EDIT2: Here's my full component code in case that helps:

export function WorkoutDisplayScreen({ navigation }) {
  const [workouts, setWorkouts] = React.useState([]);
  const workoutsArray = [];
  const [isFirstLoad, setIsFirstLoad] = useState(true);
  const [exerciseData, setExerciseData] = useState({});

  async function getData(exercise) {
    await getDoc(exercise).then((doc) => {
      setExerciseData(doc.data());
    });
  }

  const getWorkouts = async () => {
    console.log('Firebase READ & WRITE');
    const workoutsCollection = collection(db, 'Users', auth.currentUser.uid, 'Workouts');
    const workoutsDocs = await getDocs(workoutsCollection);
    workoutsDocs.forEach((workout) => {
      workoutsArray.push(workout.data());
    });

    setWorkouts(workoutsArray);
  };

  useEffect(() => {
    console.log('Rendering...');
    if (isFirstLoad) {
      getWorkouts();
    }
    setIsFirstLoad(false);
  }, []);

  return (
    <ScreenContainer>
      <View style={styles.headerrow}>
        <TouchableOpacity
          onPress={() => {
            navigation.goBack();
          }}
        >
          <AntDesign name="leftcircle" size={24} color="black" style={styles.headericon} />
        </TouchableOpacity>
        <Text style={styles.header}>My Workouts</Text>
      </View>

      {workouts &&
        workouts.map((workout) => {
          return (
            <View key={workout.id}>
              <View style={styles.workoutnamecontainer}>
                <Text style={styles.workoutnametext}>{workout.name}</Text>
              </View>
              {workout.exercises.map((exercise, index) => {
                getData(exercise);

                return (
                  <View style={styles.exercisecontainer} key={index}>
                    <Text style={styles.exercisetext}>{exerciseData.name}</Text>
                    <Text style={styles.exercisetext}>{exerciseData.type}</Text>
                    <Text style={styles.exercisetext}>{exerciseData.weight}</Text>
                    <Text style={styles.exercisetext}>{exerciseData.reps}</Text>
                  </View>
                );
              })}
            </View>
          );
        })}
    </ScreenContainer>
  );
}

EDIT3: I can now log the data in the correct format with, but trying to render it on screen is still not working correctly.

  const getWorkouts = async () => {
    console.log('Firebase READ & WRITE');
    const workoutsCollection = collection(db, 'Users', auth.currentUser.uid, 'Workouts');
    const workoutsSnapshot = await getDocs(workoutsCollection);
    let workoutsDocs = workoutsSnapshot.docs;
    workoutsDocs.forEach((workout) => {
      workoutsArray.push(workout.data());
    });
    setWorkouts(workoutsArray);

    workouts.map((workout) => {
      workout.exercises.forEach((exercise) => {
        getDoc(exercise).then((doc) => {
          console.log(workout.name + ':');
          console.log(doc.data());
        });
      });
    });
  };

Upvotes: 1

Views: 276

Answers (1)

HittuDesai
HittuDesai

Reputation: 361

I believe you should do all the data-fetching i.e. for all the workouts and every exercise in each workout in the useEffect itself. I have never seen code where data fetching is done inside the JSX part of the function. I am highly skeptical about it.

Also, one thing I noticed while looking through your code is that you are iterating over the snapshot instead of docs in the the snapshot inside your getWorkouts() function. It should be as follows:

const getWorkouts = async () => {
  console.log('Firebase READ & WRITE');
  const workoutsCollection = collection(db, 'Users', auth.currentUser.uid, 'Workouts');
  const workoutsSnapshot = await getDocs(workoutsCollection);
  workoutsDocs = workoutsSnapshot.docs; // This line must be added to your code
  workoutsDocs.forEach((workout) => {
    workoutsArray.push(workout.data());
  });

  setWorkouts(workoutsArray);
};

Upvotes: 1

Related Questions