Search code examples
reactjsmongodbmongoosemern

Fetch data from two Mongodb databases in MERN stack


In a MERN stack app, I'm trying to fetch data from two Mongodb databases and store them in context state, but I can only ever get one or the other depending on the order they are in.

App.js. In this case, the second useEffect hook works but not the first.

import { useEffect } from 'react';
import { useClosetContext } from './hooks/useClosetContext';

function App() {
  const { dispatch } = useClosetContext()
  
  useEffect(() => {

    const fetchCloset = async () => {
      const response = await fetch('/api/closet')
      const json = await response.json()

      if (response.ok) {
        dispatch({type:'SET_CLOSET', payload: json})
      }
    }

    fetchCloset()
  
  }, [dispatch])

  useEffect(() => {

    const fetchSavedLists = async () => {
        const response = await fetch('/api/checklist')
        const json = await response.json()
  
        if (response.ok) {
          dispatch({type:'SET_CHECKLISTS', payload: json})
        }
      }

    fetchSavedLists()

  }, [dispatch])

  return (
    <div className="App">...</div>
  );
}

export default App;

I have tried putting both the fetchCloset() and fetchSavedLists() functions in the same useEffect hook, but I get the same results.

I also tried this, but didn't get anything:

useEffect(() => {
  const fetchData = () => {
      Promise.all([
        fetch('/api/closet'),
        fetch('/api/checklist')
      ]).then(([closet, checklist]) => {
        dispatch({type:'SET_CLOSET', payload: closet})
        dispatch({type:'SET_CHECKLISTS', payload: checklist})
      }).catch(err => {
        console.log(err)
      })
    }

  fetchData()
}, [dispatch])

Here is my Context file:

import { createContext, useReducer } from "react";

export const ClosetContext = createContext()

export const closetReducer = (state, action) => {
  switch (action.type) {
    case 'SET_CLOSET':
      return {
        closet: action.payload
      }
    case 'CREATE_GEAR':
      return {
        closet: [action.payload, ...state.closet]
      }
    case 'SET_CHECKLISTS':
      return {
        checklists: action.payload
      }
    case 'CREATE_CHECKLIST':
      return {
        checklists: [action.payload, ...state.checklists]
      }
    default:
      return state
  }
}

export const ClosetContextProvider = ({ children }) => {
  const [state, dispatch] = useReducer(closetReducer, {
    closet: null,
    checklists: null
  })

  return (
    <ClosetContext.Provider value={{...state, dispatch}}>
      { children }
    </ClosetContext.Provider>
  );
}

Context Hook:

import { useContext } from "react";
import { ClosetContext } from "../context/ClosetContext";

export const useClosetContext = () => {
  const context = useContext(ClosetContext)

  if (!context) {
    throw Error('error')
  }

  return context
}

I don't think there is anything wrong with the backend because I can fetch the data separately. Is there any way I can fetch both databases and set them to Context state?


Solution

  • The problem is in your reducer:

        case 'SET_CLOSET':
          return {
            closet: action.payload
          }
        case 'SET_CHECKLISTS':
          return {
            checklists: action.payload
          }
    

    You may want something like:

        case 'SET_CLOSET':
          return {
            ...state,
            closet: action.payload
          }
        case 'SET_CHECKLISTS':
          return {
            ...state,
            checklists: action.payload
          }
    

    Otherwise SET_CLOSET will erase checklists and SET_CHECKLISTS will erase closet.