Home > Mobile >  Why are some properties of my Firebase database undefined?
Why are some properties of my Firebase database undefined?

Time:12-05

When my character card component loads I receive the below error.

TypeError: Cannot read properties of undefined (reading 'attack')

The main thing that confuses me is that the character name(userData.name) and profile image(userData.icon) are not undefined. All properties are stored in the same Firebase database. I can't figure out why 2 out of the 4 properties I'm trying to access are undefined. I get the same error if trying to access the defense level.

I'm following the Firebase docs for accessing the database under the read data section: https://firebase.google.com/docs/database/web/read-and-write

Here is my component code:

import { Card, Container, Row, Col } from "react-bootstrap";
import { auth } from "../../components/Utils/firebase";
import { getDatabase, onValue, ref } from "firebase/database";
import { useState, useEffect } from "react";

import classes from "./charCard.module.css";

const CharCard = () => {
  const userId = auth.currentUser.uid;
  const db = getDatabase();
  const [userData, setUserData] = useState([]);

  //acceses Firebase
  useEffect(() => {
    const userRef = ref(db, "/Users/"   userId);
    onValue(userRef, (snapshot) => {
      const data = snapshot.val();
      setUserData(data);
      console.log(data);
    });
  }, []);

  return (
    <Card className={classes.charCard}>
      <h2 className={classes.charName}>{userData.name}</h2>
      <Container>
        <Row>
          <Col>
            <img className={classes.charIcon} src={userData.icon} />
          </Col>
          <Col>
            <Col className={classes.stats}>
              Attack: {userData.skills.attack.level}
            </Col>
            <Col className={classes.stats}>
              Defense: {userData.skills.defense.level}
            </Col>
          </Col>
        </Row>
      </Container>
    </Card>
  );
};

export default CharCard;

Here is the layout of the Firebase Database

"Users": {
    "fAzbjOaHflTTos45CDRu1ITrZs23": {
      "icon": "crossedswords.png",
      "name": "Waffle",
      "skills": {
        "attack": {
          "level": 1,
          "name": "Attack"
        },

If I comment out the below and let the page load first, and then un-comment it pulls the correct value of "1".

<Col className={classes.stats}>
      Attack: {userData.skills.attack.level}
</Col>

I've also tried re-writing how I access the Firebase database to not use useEffect, but that hasn't changed anything.

I have a feeling that I'm missing something pretty basic.

Repo is public at https://github.com/nschroder26/idleWeb if more context is needed.

CodePudding user response:

I read your code.

There are 3 problems with your code.

  1. The initial value of userData should be an empty object not an array.

  2. You should check if userData is set and then render your UI.

  3. You wrote your onValue function in useEffect without any dependency. It means that it runs only once. At that time, auth.currentUser is undefined so userId is undefined too. So you get null for the user data.

In order to fix the issue, you should write your logic inside onAuthStateChanged.

This will fix your problem:

import { Fragment, useState, useEffect } from "react";
import { auth } from "../../components/Utils/firebase";
import { getDatabase, onValue, ref } from "firebase/database";
import CharCard from "../../components/CharCard/charCard";
import { onAuthStateChanged } from "firebase/auth";

const Character = () => {
  const db = getDatabase();
  const [userData, setUserData] = useState({});

  //caputures current user from firebase db
  useEffect(() => {
    onAuthStateChanged(auth, (currentUser) => {
      if(currentUser) {
        const userId = currentUser.uid;
        const userRef = ref(db, "/Users/"   userId);
        onValue(userRef, (snapshot) => {
          const data = snapshot.val();
          setUserData(data);
          console.log(data)
        });
      }
    })
  }, []);

  return (
    <Fragment>
      <CharCard
        name={userData?.name}
        icon={userData?.icon}
        attack={userData?.skills?.attack?.level}
        defense={userData?.skills?.defense?.level}
      />
    </Fragment>
  );
};

export default Character;
  • Related