Home > Blockchain >  Unable to Map Array of MongoDB Documents Returned from Backend into React Components
Unable to Map Array of MongoDB Documents Returned from Backend into React Components

Time:02-02

I am trying to get my front-end to call to the back-end for all the "blog posts" that are stored in my MongoDB database. At the moment there is only one document for testing.

On the backend I have this api endpoint:

app.get("/api/blogs", async (req, res) => {
    console.log("Getting blog items...");
    try{
        const blogs = await blogActions.getBlogItems();
        res.status(200).json({blogs});
    } catch (err) {
        console.log(err)
    }
});

This calls to a separate JS file with this function:

const { MongoClient } = require('mongodb');
const uri = 'mongodb://localhost:27017';
const client = new MongoClient(uri);

const connection = async () => {
    try {
        const database = client.db('personalwebsite');
        const blogs = database.collection('blogs');

        return blogs;
    } catch (err) {
        console.log(err);
    }
}

const getBlogItems = async () => {
    const conn = await connection();
    try {
        return await conn.find({}).toArray();
    } catch (err) {
        console.log(err);
    }
};

Then in my React front-end I am trying to take the returned array and set it to an Array there in order to map over it and create a new BlogItem component for each blog returned from the database:

import { useState, useEffect } from "react";
import Navbar from "../components/Navbar.tsx";
import BlogItem from "../components/BlogItem.tsx";
import '../styles/Blog.css';

export default function Blog () {
    const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
    const [isAdmin, setIsAdmin] = useState<boolean>(false);
    const [token, setToken] = useState<string>('');
    const [blogs, setBlogs] = useState([]);

    useEffect(() => {
        setToken(localStorage.getItem('token'));

        async function checkToken () {
            const response = await fetch('/api/token', {
              method: 'POST',
              headers: {
                  'Content-type': 'application/json',
                  'Authorization': `Bearer ${token}`
              }
            });
            if (response.ok){
              const jsonResponse = await response.json();
              if (jsonResponse.delete){
                localStorage.clear();
                return false;
              }
              return true;
            } else {
              console.log("Failed to fetch status of the User Login Session.");
            }
        }

        async function checkIfAdmin () {
            const response = await fetch('/api/users/permissions', {
                method: 'POST',
                headers: {
                    'Content-type': 'application/json',
                    'Authorization': `Bearer ${token}`
                }
            });
            if(response.ok) {
                const jsonResponse = await response.json();
                if (jsonResponse.role === 'admin') {
                    setIsAdmin(true);
                } else {
                    setIsAdmin(false);
                }
            }
        }

        async function getBlogItems () {
            try {
                const response = await fetch('/api/blogs');
                const data = await response.json();
                console.log("Before setBlogs", data.blogs)
                if(data.blogs.length > 0) {
                    setBlogs(data.blogs);
                }
            } catch (err) {
                console.log(err);
            }
        }
    
        if (token) {
            checkToken().then(isValid => {
                if (!isValid) return;
                checkIfAdmin();
            });
        }
        
        getBlogItems();

    }, [])

    console.log("After setBlogs", blogs);

    return (
        <div className="App">
          <Navbar />
          <main className="main-content">
            <div className="blogs-container">
                {blogs.length > 0 ? (
                    blogs.map((blog) => (
                        <BlogItem
                            key={blog._id}
                            title={blog.title}
                            shortDesc={blog.shortDesc}
                            imgSrc={blog.imgSrc}
                            pubDate={blog.pubDate}
                        />
                    ))
                ) : (
                    <div>Loading...</div>
                )}  
            </div>
            <div className="most-popular"></div>
          </main>
        </div>
    );
}

I have tried quite a few different methods for trying to get this to work correctly. At first I thought it was just a problem with the data not being returned quickly enough but even after getting the code to wait for the data to be returned and getting the Array to set. I get an error that Objects are not valid as a React child.

This is meant to be an array of Objects so that I can access the properties of each object for the elements in the component but I cannot get it to work for the life of me. I spent awhile using ChatGPT to try and get some progress out of it but this seems to be a problem that requires human intervention instead.

CodePudding user response:

So this is annoying but the issue wasn't with the rest of my code but actually that the BlogItem component had the props wrapped only in parentheses.

Here is the component:

export default function BlogItem ({title, shortDesc, imgSrc, pubDate}) {
    return (
    <div className="blog-item">
        <img src={imgSrc} className="blog-image" />
        <h1 className="blog-title">{title === "" ? "Placeholder Title" : title }</h1>
        <p className="blog-short-desc">{shortDesc}</p>
        <p className="blog-date">{pubDate}</p>
    </div>
    );
}

The fix was that title, shortDesc, imgSrc, pubDate. All needed to be wrapped in Curly Braces. It now works :\

  • Related