1

I want to use the React Hooks functionality in combination with Firebase. But now when the data is set, the result is only visible when the DOM is being updated. My current code is:

import React, { useState, useEffect } from 'react';
import firebase, { getPivotFunction } from '../../firebase';

/**
* Return a styled component
*/
const ListCards = () => {
  const [data, setData] = useState([]);

  const userListsRef = firebase
    .database()
    .ref('userLists')
    .child('1234d343f');

  useEffect(() => {
    (async function() {
       try {
         const response = await getPivotFunction(userListsRef, 'lists');
         setData(response);
       } catch (e) {
         console.error(e);
       }
    })();
  }, []);

 /**
 * Return all list cards
 */
 return (
  <ul>
    {data.map(item => (
      <li key={item.time}>dummy text</li>
    ))}
  </ul>
 );
};

When the page is beeing rendered for the first time the 'dummy text' is not displayed, only when there is an update beeing triggered. My goal is to let 'dummy text' apear when the page is done loading and data not having a length of 0.

In this case getPivotFunction contains:

/** Get FireBase data based on a pivot table */
const getPivotFunction = (ref, target) => {
  let dataArray = [];

  ref.once('value', userInfo => {
    userInfo.forEach(function(result) {
      firebase
        .database()
        .ref(target)
        .child(result.key)
        .on('value', snapshot => {
          dataArray.push(snapshot.val());
        });
    });
  });

  return dataArray;
};

Please let me know

Ronald Zwiers
  • 780
  • 2
  • 10
  • 25

2 Answers2

2

Your getPivotFunction is an asynchronous function which relies on callback and this using async await on isn't the right approach. You instead need to have a callback

/** Get FireBase data based on a pivot table */
const getPivotFunction = (ref, target, callback) => {
  const dataArray= [];
  ref.once('value', userChats => {
    var i = 0;
    userChats.forEach(function(result) {
      firebase
        .database()
        .ref(target)
        .child(result.key)
        .on('value', snapshot => {
          i++;
          dataArray.push(snapshot.val());
          if(i === userChats.length) {
             callback(dataArray)
          }
        });
    });
  });
};

and use it like

/**
* Return a styled component
*/
const ListCards = () => {
  const [data, setData] = useState([]);

  const userListsRef = firebase
    .database()
    .ref('userLists')
    .child('1234d343f');

  useEffect(() => {
      getPivotFunction(userListsRef, 'lists', (response) => {
         setData(response);
      });
  }, []);

 /**
 * Return all list cards
 */
 return (
  <ul>
    {data.map(item => (
      <li key={item.time}>dummy text</li>
    ))}
  </ul>
 );
};
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
0

Hooks aren't meant to work with async functions like that. Something like this should work:

const ListCards = () => {
  const [data, setData] = useState([]);
  const [loaded, setLoaded] = useState(false);

  ... 

  useEffect(() => {
    getPivotFunction(userListsRef, 'lists')
    .then(data => { setData(data); setLoaded(true)});
  }, []);
};

Then only render when loaded is true.

Colin Ricardo
  • 16,488
  • 11
  • 47
  • 80
  • This isnt correct. The OP used a way to write async functions within useEffect. You can check this https://stackoverflow.com/questions/53332321/react-hook-warnings-for-async-function-in-useeffect-useeffect-function-must-ret/53332372#53332372 – Shubham Khatri Mar 24 '19 at 14:02
  • Yes, you can do that but it's not the recommended way to do it. The question you linked even says it's a "workaround". – Colin Ricardo Mar 24 '19 at 14:03
  • Its an anonymous function which is called when the effect is run, and it allows you to use await – Shubham Khatri Mar 24 '19 at 14:04