I have a ReactTS-App and I pass a prop via Router-Dom-Props to another component. The problem here is that I can use meal.name alone, but if I use meal.food with it or meal.food alone it doesnt work anymore.
Uncaught TypeError: meal.food is undefined
I checked TypeScript- & useEffect-errors, but I didnt find a solution yet.
And are the props loaded before the first render?
UPDATE I can print the meal.food when I use it like that location.state.meal.food
but I cannot use it from the useState after I set it - I console.log it in the useEffect.
Meal.tsx
return (
<Link className="Meal Link" to={`/MealDetails/${meal.id}`} state={{ meal: meal }}>
<div className="MealIconMealName">
<div className="MealName">{meal.name}</div>
</div>
</Link>
);
};
MealDetails.tsx
const MealDetails = () => {
const location = useLocation();
const navigate = useNavigate();
const [meal, setMeal] = useState(Object);
useEffect(() => {
if (location.state) {
if (location.state.meal) {
setMeal(location.state.meal);
console.log("useEffect" meal);
}
}
return () => {};
}, [location]);
return (
<div className="MealDetails">
<header>
<div className="HeaderText">{meal.name}</div>
</header>
<div className="MealDetailsCalorieCounter"></div>
{meal.food.map((meal : any) => (
<Food
key={meal.id}
/>
))}
</div>
);
};
Example meal-Object out of the .json file
{
"id": 1,
"name": "Breakfast",
"food": [
{
"name": "Waffeln",
"kcal" : 505
}
],
"calories": 505
}
CodePudding user response:
I don't think useEffect here is necessary. You can get the meal directly from the location object.
const MealDetails = () => {
const location = useLocation();
const { meal } = location.state;
return (
<div className="MealDetails">
<header>
<div className="HeaderText">{meal.name}</div>
</header>
<div className="MealDetailsCalorieCounter"></div>
{meal.food.map((meal : any) => (
<Food
key={meal.id}
/>
))}
</div>
);
};
CodePudding user response:
Issue
The issue here is it seems that the meal.food
is undefined on the initial render prior to the useEffect
hook updating the local component state from the route state (i.e. from location.state
).
Solution
It's considered a React anti-pattern to store passed data and/or derived "state" in local component state. Just consume the passed location.state.meal
route state directly. You should also use defensive programming patterns in case a user navigates to the route rendering MealDetails
from anywhere else other than the link that passes the route state.
Example:
const MealDetails = () => {
const { state } = useLocation(); // <-- access state
const navigate = useNavigate();
const meal = state || {}; // <-- provide fallback value
return (
<div className="MealDetails">
<header>
<div className="HeaderText">{meal.name}</div>
</header>
<div className="MealDetailsCalorieCounter"></div>
{(meal.food }} []).map((meal: any) => ( // <-- provide fallback array
<Food key={meal.id} />
))}
</div>
);
};