2

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?

Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
Broski-AC
  • 739
  • 6
  • 12

1 Answers1

1

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
Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
  • Thanks for your help. I'm a bit confused by your answer, as you say that loading should be in state, but then you make use of loading in `useRef` in your response. If I change the code instead to read ```if (dataDash.current !=== undefined) {setLoading(loading = false)}``` I still receive that same error. – Broski-AC Nov 09 '20 at 14:55
  • Sorry. You're right I just copy/pasted this part. I've updated my answer. Also, I'm not sure `dataDash` also should be a `Ref` but this is a different topic.. – Mosh Feu Nov 09 '20 at 14:58
  • Happens to the best of us. It now says that `useEffect has missing dependency 'props.user.' Would it make sense to include that in the square brackets at the bottom? What exactly do those indicate? – Broski-AC Nov 09 '20 at 15:01
  • The rule of thumb of what needed to be in the dependencies array - `all values from the component scope (such as props and state) that change over time and that are used by the effect` (https://reactjs.org/docs/hooks-effect.html) so, yes, it makes sense. Consider if the user would change, you probably want to fetch the data again. – Mosh Feu Nov 09 '20 at 17:34
  • Sure :) glad to hear. – Mosh Feu Nov 09 '20 at 19:17