Why is my console.log showing an empty array inside my function, but before and after the function showing the correct value?

Can anyone explain why my console.log(‘in function’, randomMeals) is returning an empty array but the console.logs before and after the function have the correct value? I’m trying to do a drag and drop from one array to the other. Currently the only way to fix the problem is to comment in OR out anything, basically alter the page so that the page rerenders and then I’m able to successfully drag and drop from randomMeals to dropCalendar.

Primary component I’m having the issue in:

import React, { useState } from 'react';
import './HomepageCalender.css';
import { Meal } from '../../types';
import RecipeCard from '../RecipeCard/RecipeCard';
import { useDrop } from 'react-dnd';

interface HomepageCalenderProps {
  randomMeals: Meal[];
  toggleLock: (idMeal: string) => void;
}

interface DroppedMeal {
  id: string;
}

const HomepageCalender: React.FC<HomepageCalenderProps> = ({ randomMeals, toggleLock }) => {
  const [dropCalendar, setDropCalendar] = useState<Meal[]>([]);

  const [{ isOver }, drop] = useDrop(() => ({
    accept: 'recipe-card',
    drop: (droppedMeal: DroppedMeal) => {
      console.log('meal', droppedMeal);
      addRecipeCardToBoard(droppedMeal.id);
      // console.log('hook', randomMeals)
    },
    collect: (monitor) => ({
      isOver: !!monitor.isOver(),
    }),
  }));
  
  console.log('before funtion', randomMeals)
  const addRecipeCardToBoard = (id: string) => {
    console.log('in function', randomMeals)

    const filteredRandomMeals = randomMeals.filter((meal) => meal.idMeal === id);

    const uniqueDroppedMeals = filteredRandomMeals.filter(
      (meal, index, self) =>
      index === self.findIndex((m) => m.idMeal === meal.idMeal)
      );
    
      setDropCalendar((prevDropCalendar) => [...prevDropCalendar, ...uniqueDroppedMeals]);
  };
  console.log('after function', randomMeals)

  return (
    <div className="homepage-calendar-container">
      <h4 className="date">Date</h4>
      <div className="droppable-area" ref={drop}>
        {dropCalendar.map((meal) => (
          <RecipeCard
            key={meal.idMeal}
            idMeal={meal.idMeal}
            strMeal={meal.strMeal}
            strMealThumb={meal.strMealThumb}
            toggleLock={toggleLock}
            locked={true}
          />
        ))}
      </div>
    </div>
  );
};

export default HomepageCalender;

Parent component for reference:

import React, { useEffect, useState } from 'react';
import Header from '../Header/Header';
import WelcomeBox from '../WelcomeBox/WelcomeBox';
import Footer from '../Footer/Footer';
import RandomMealForm from '../RandomMealForm/RandomMealForm';
import HomepageCalender from '../HomepageCalender/HomepageCalender';
import { fetchSingleRandomRecipe } from '../../apiCalls';
import { RandomMealProps } from '../../types';

const Homepage: React.FC = () => {
  const [numberOfMeals, setNumberOfMeals] = useState<number>(5);
  const [randomMeals, setRandomMeals] = useState<RandomMealProps[]>([]);

  const toggleLock = (idMeal: string) => {
    const updatedRandomMeals = randomMeals.map((meal) => {
      if (meal.idMeal === idMeal) {
        return { ...meal, locked: !meal.locked }; // Create a new object
      }
      return meal;
    });

    setRandomMeals(updatedRandomMeals);
  };

  useEffect(() => {
    const fetchData = async () => {
      try {
        const fetchedMeals = [];
        for (let i = 0; i < numberOfMeals; i++) {
          const data = await fetchSingleRandomRecipe();
          if (data?.meals) {
            const newMeals = data.meals.map((meal: RandomMealProps) => ({
              ...meal,
              locked: false,
            }));
            fetchedMeals.push(...newMeals);
          }
        }

        const uniqueMeals = fetchedMeals.filter(
          (meal, index, self) =>
            index === self.findIndex((m) => m.idMeal === meal.idMeal)
        );

        setRandomMeals(uniqueMeals);
      } catch (error) {
        console.error(error);
        // Handle errors here if needed
      }
    };

    fetchData();
  }, [numberOfMeals]);

  useEffect(() => {
    console.log('homepage', randomMeals);
  }, [randomMeals]);

  return (
    <div className="homepage">
      <Header />
      {randomMeals.length > 0 && (
        <WelcomeBox randomMeals={randomMeals} toggleLock={toggleLock} />
      )}
      <RandomMealForm
        numberOfMeals={numberOfMeals}
        setNumberOfMeals={setNumberOfMeals}
        setRandomMeals={setRandomMeals}
        randomMeals={randomMeals}
        toggleLock={toggleLock}
      />
      <HomepageCalender randomMeals={randomMeals} toggleLock={toggleLock} />
      <Footer />
    </div>
  );
};

export default Homepage;

I tried putting a useCallback around the function, putting a useCallback and useMemo in the parent component so that the data would persist but no luck. And I already have a useEffect in the parent component to make the randomMeals array persist. The array is available everywhere else in the primary component, just not in the addRecipeCardToBoard function. Any suggestions would be helpful TY!

  • console evaluates the logged value of object when you inspect it in the console, not when it is logged, so you can see this sort of thing happen. do something like console.log('before funtion', randomMeals.length, randomMeals) and you’ll see what’s happening. Alternatively console.log('before funtion', [...randomMeals])

    – 

  • I’m guessing this is because useDrop uses the addRecipeCardToBoard function from your first component render, and that function has “closed over” the initial randomMeals value (an empty array), and I’m guessing the drop callback isn’t updated on future renders. One way around this is not to call your function inside drop, since according to the docs you’re only supposed to use this method to change the drop return type. Instead, call your function in response to the drop event.

    – 




  • Oh, apparently useDrop also takes deps which isn’t documented, so you calling useDrop({...}, [randomMeals]); will probably also “fix” it.

    – 

Leave a Comment