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. Alternativelyconsole.log('before funtion', [...randomMeals])
I’m guessing this is because
useDrop
uses theaddRecipeCardToBoard
function from your first component render, and that function has “closed over” the initialrandomMeals
value (an empty array), and I’m guessing thedrop
callback isn’t updated on future renders. One way around this is not to call your function insidedrop
, 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 callinguseDrop({...}, [randomMeals]);
will probably also “fix” it.