Grid / Array of documents not loading on first click

Viewed 38

I am trying to load an array of documents into a grid (in React.js) from Firebase FireStore, but it isn't displaying on the first tap of the navigation button. Even if I add a loading component, that displays before the grid - it still doesn't work

getAuthor func

const getAuthor = async (uid) => {
    const author = await getDoc(doc(getFirestore(app), 'StripeCustomers', uid));
    if (author.exists()) {
        return author.data();
    }
    return null;
}

getGrid func

const getGrid = async (type) => {
    let gridData = [];
    const grid = await getDocs(collection(getFirestore(app), type));
    grid?.docs.map(item => {
        getAuthor(item.data().Author.id).then(author => {

            gridData.push({
                title: item.data().title,
                link: "/materials/type=" + type + "/uuid=" + item.id,
                thumbnail: "#",
                type: type,
                authors: [{
                    name: author.FirstName + " " + author.LastName,
                    imageUrl: author.profileUrl,
                    profileUrl: "/user/id=" + author.id
                }]

            });
        });
    });
    return gridData;
}

Partial:

const Material = ({ info }) => {
    return (
        <Link to={info.link} className="flex flex-col dark:black hover:border hover:bg-gray-100 dark:hover:bg-slate-900 hover:border-gray-200 dark:hover:border-slate-800 rounded-2xl cursor-pointer overflow-hidden box-border">
            <img src={info.thumbnail} className="thumbnail aspect-video rounded-2xl" />
            <div className="flex items-center p-3 space-x-4">
                {/* <Link to='#'><img src={info.authors[0].imageUrl} className="w-12 block aspect-square rounded-full" /></Link> */}
                <div className="flex flex-col">
                    <h3 className="text-xl font-bold">{info.title}</h3>
                    {info.authors.map((author, index) => (<p key={index} className="text-sm text-gray-500">{author.name}</p>))}
                </div>
            </div>
        </Link>
    );
}

Main:

const Materials = ({ groupBy }) => {
    const [grid, setGrid] = useState([]);
    
    useEffect(() => {
        getGrid(groupBy).then(gridn => {
            setGrid(gridn);
        });
    }, []);

    return (
        <div className="w-full flex flex-col justify-content items-center m-auto">
            <div className="materials w-11/12 lg:p-5 pt-5 grid 2xl:grid-cols-4 xl:grid-cols-3 lg:grid-cols-2 gap-4 gap-y-6">
                {grid && grid?.map((i) => (<Material key={i} info={i} />))}
            </div>
        </div>
    )
}
1 Answers

If you check in a debugger (or add some logging), you'll see that return gridData runs before any of your gridData.push({ ... }) calls happen, and you can't return something that hasn't loaded yet.

A simple solution is to use Promise.all, like this:

const getGrid = async (type) => {
  const grid = await getDocs(collection(getFirestore(app), type));
  const promises = grid?.docs.map(item => getAuthor(item.data().Author.id));
  const authors = await Promise.all(promises);
  const girdData = authors.map(author => {
    return {
        title: item.data().title,
        link: "/materials/type=" + type + "/uuid=" + item.id,
        thumbnail: "#",
        type: type,
        authors: [{
          name: author.FirstName + " " + author.LastName,
          imageUrl: author.profileUrl,
          profileUrl: "/user/id=" + author.id
        }]
    }
  });
  return gridData;
}
Related