Trying to render Firestore information in React component - utilization of useRef() not re-rendering and useState() raises error

Viewed 115

I am currently working with the following React component:

function Dashboard(props) {
  const loadingDash = useRef(true);
  const dataDash = useRef([]);
  // var [loading, setLoading] = useState(true)
  // var [data, setData] = useState([])

  // Style info goes here - unimportant to question at hand

  var userName,
    userEmail,
    planRank,
    designRank,
    implementRank,
    testRank,
    maintRank;

  useEffect(() => {
    db.collection('Users')
      .doc(props.user)
      .onSnapshot({ includeMetadataChanges: true }, function (userInfo) {
        dataDash.current = userInfo.data();
        console.log('dataDash: ', dataDash);
        if (dataDash.current !== undefined) {
          loadingDash.current = false;
          console.log('Loading dash: ', loadingDash);
        }
      });
  });

  if (loadingDash) {
    return <h1>We're watching, and we're waiting</h1>; // Activation spinner here
  }
  return (
    <div className={classes.app}>
      <NavBar loggedIn={props.isLoggedIn} />

      <div className={classes.page}>
        <img className={classes.photo} src={defaultprofile} alt={'UserPhoto'} />
        <h1 className={classes.profilename}>UserName</h1>
        <h4 className={classes.profileinfo}>Email: email</h4>
        <h4 className={classes.profileinfo}>Rank: "Rank"</h4>
      </div>

      <div className={classes.page}>
        <h1 className={classes.progressname}>Learning Progress</h1>
        <p className={classes.progressinfotop}>Planning - 75%</p>
        <LinearProgress
          variant="determinate"
          className={classes.progressbar}
          value={75}
        />
        <p className={classes.progressinfo}>Design - 50%</p>
        <LinearProgress
          variant="determinate"
          className={classes.progressbar}
          value={50}
        />
        <p className={classes.progressinfo}>Implementation - 100%</p>
        <LinearProgress
          variant="determinate"
          className={classes.progressbar}
          value={100}
        />
        <p className={classes.progressinfo}>Testing & Deployment - 75%</p>
        <LinearProgress
          variant="determinate"
          className={classes.progressbar}
          value={75}
        />
        <p className={classes.progressinfo}>Maintenance - 25%</p>
        <LinearProgress
          variant="determinate"
          className={classes.progressbarbottom}
          value={25}
        />
      </div>

      <div className={classes.page}>
        <p className={classes.continuetext}>
          Want to continue where you left off, click below to continue!
        </p>

        <button className={classes.buttons}>
          <Link to="/LearnMore" className={classes.buttonlinks}>
            Continue
          </Link>
        </button>
      </div>

      <div className={classes.footer}>
        Copyright: Allison Broski, Shelby McKay, Maurice Fuentes, Timothy
        Carpenter, Tanner Porteous
        <p>Oakland University</p>
      </div>
    </div>
  );
}

export default Dashboard;

My basic problem is that I am trying to retrieve information from a Firestore database, and then display individual attributes onto the page. However, I've run into the issue of fetching the data asynchronously. In an attempt to fix this, I referenced this and one which suggested using React state. This is what my component is based of. However, when I used the useState() feature in React, it raised the following error:

Assignments to the 'loading' variable from inside React Hook useEffect will be lost after each render. To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property. Otherwise, you can move this variable directly inside useEffect.

In an attempt to address the error, I changed it to utilize useRef() functionality. However, now the component won't re-render when loading changes, because according to the documentation this isn't a feature of useRef(). Every other tutorial I have looked at says to utilize useState() but I can't apparently do that.

Are there any suggestions on ways to address these issues, such that I can accurately render data from a Firestore database?

1 Answers

The loading variable should be in the state (useState). I think that the error message shows because there are no dependencies in the useEffect call so basically every change re-render the whole component on every prop change and indeed, it will override the render value.

I believe that you want to run the listener only once the component first render. In this case, you should pass an empty array as second argument (the dependencies)

useEffect(() => {
  db.collection('Users').doc(props.user).onSnapshot({
      includeMetadataChanges: true
    },
    function(userInfo) {
      dataDash.current = userInfo.data()
      console.log('dataDash: ', dataDash)
      if (dataDash.current !== undefined) {
        setLoading(false);
      }
    })
}, []); // <-- here
Related