0

Tech stack: Next.js, React, MongoDB, Mongoose, Zustand (global state)

I am saving objects (Flashcards in JSON format) in MongoDB and fetch the data through Mongoose. I use Mongoose's idea of Model and Schema.

I want to load all data from MongoDB into a global state variable (Zustand) called mainList conditionally with useEffect, as soon as the user arrives at the main page. This way I want to minimize the overall communication with MongoDB and also for quicker conditional renderings on various pages. If this makes sense... I don't know, as I am still a beginner.

useVocabStore is the global hook I created, which holds the variable mainList, including other functions like fetchAllCards, which I use to async pull data via getAllFlashcards from MongoDB, into said global state variable mainList.

Problem

Within useEffect I am making an async function call of fetchAllCards, because ultimately this calls MongoDB. Unfortunately I am getting this error:

Uncaught TypeError: Cannot read properties of undefined (reading 'Flashcard')

My understanding of this error is that when fetchAllCards calls MongoDB, the data it receives is for some reason undefined.

git@github.com:j-eick/vocab-dojo.git

Question

Why does that async call of MongoDB via global state hook within useEffect cause undefined? What am I missing here?


This my useVocabStore- hook:

import create from 'zustand';

import {getAllFlashcards} from '../services/vocabServices';

const vocabStore = set => ({
    mainList: [],

    // pulls cards from MongoDB
    fetchAllCards: async () => set({mainList: await getAllFlashcards()}),

    addVocab: newCard =>
        set(state => ({
            mainList: [...state.mainList, newCard],
        })),

    // other code...
});

export const useVocabStore = create(vocabStore);

useEffect calls MongoDB initially for one time only. Apparently useEffect cannot make an async call (source), which is why I am making an async call within useEffect.

useEffect(() => {
        const fetchCards = async () => {
            await fetchAllCards();
        };
        fetchCards().catch(console.error);
    }, []);

To briefly touch the otherwise functioning getAllFlashcards. There are other pages in this next.js project where I also async call getAllFlashcards without any problems:

//other imports...
import {getAllFlashcards} from '../../services/vocabServices';

export async function getStaticProps() {
    const allFlashcards = await getAllFlashcards();

    return {
        props: {allFlashcards},
    };
}

export default function VocabListPage({allFlashcards}) {
//code...

This is the complete code of the main page:

import Head from 'next/head';
import {useEffect} from 'react';

import Layout from '../components/Layout';
import StartSession_noCards from '../components/Links/StartSessionLink_noCards';
import StartSession_withCards from '../components/Links/StartSessionLink_withCards';
import InfoModalPopupSmall from '../components/Modals/InfoModalPopup/InfoModalPopupSmall';
import Textfield from '../components/Texfield/Textbox';
import TextfieldArea from '../components/TextfieldArea';
import Title from '../components/Title/index';
import {useToggleStore} from '../hooks/useToggleStore';
import {useVocabStore} from '../hooks/useVocabStore';

console.clear();

export default function HomePage() {
    const fetchAllCards = useVocabStore(state => state.fetchAllCards);
    const cardsInList = useToggleStore(state => state.cardsInList);
    const showModal = useToggleStore(state => state.showModal);
    const hide_Modal = useToggleStore(state => state.hide_Modal);
    const show_Modal = useToggleStore(state => state.show_Modal);

    const popupModalHandler = () => {
        console.log('test');
        show_Modal();
        setTimeout(() => {
            hide_Modal();
        }, 3000);
    };

    useEffect(() => {
        console.log('Test');
        const fetchCards = async () => {
            await fetchAllCards();
        };
        fetchCards().catch(console.error);
        // console.log(mainList);
    }, []);

    return (
        <Layout>
            <Head>
                <title key="title">Vocab-Dojo</title>
                <meta key="description" name="description" content="This is my project" />
            </Head>

            {/* #############   INFO MODALs   ################ */}
            {showModal && (
                <InfoModalPopupSmall variant="smallInfoModal">
                    Create flashcard first
                </InfoModalPopupSmall>
            )}

            {/* #############   TEXTFIELD AREAS   ################ */}

            <TextfieldArea variant="textarea_hardToRemember">
                <Title variant="Textbox_Title">Hard to remember</Title>
                <Textfield variant="textbox_mainpage">Example 2</Textfield>
            </TextfieldArea>
            <TextfieldArea variant="textarea_lastAdded">
                <Title variant="Textbox_Title">Last Added</Title>
                <Textfield variant="textbox_mainpage">Example</Textfield>
            </TextfieldArea>

            {/* #############   START SESSION   ################ */}
            <Title variant="Textbox_Title">Train your brain</Title>

            {/* PICK DIFFEREN STARTSESSION BUTTON BASED ON CONDITION, whether CardList is full or empty */}
            {cardsInList ? (
                <StartSession_withCards
                    variant="startSession_withCards"
                    onClick={popupModalHandler}
                />
            ) : (
                <StartSession_noCards variant="startSession_noCards" onClick={popupModalHandler} />
            )}
        </Layout>
    );
}

The Mongoose Flashcard Model:

import mongoose from 'mongoose';

const {Schema} = mongoose;

const flashcardSchema = new Schema(
    {
        frontTitle: {type: String, Number},
        frontDescription: {type: String, Number},
        backTitle: {type: String, Number},
        backDescription: {type: String, Number},
    }
    // {collection: 'flashcards'}
);

const Flashcard = mongoose.models.Flashcard || mongoose.model('Flashcard', flashcardSchema);

export default Flashcard;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
halfer
  • 19,824
  • 17
  • 99
  • 186
Josh
  • 125
  • 7
  • 1
    u r trying to make a server side call (mongoose) via client side (useeffect). You can try creating an api that returns the data u need, then u can use fetch in useEffect to get the updated data. – Someone Special May 21 '23 at 16:15

0 Answers0