React食谱收藏功能故障排查:ID获取异常与数据不显示问题
Hey there! Let's break down and fix your two core issues with the React recipe app's favorite feature one by one:
Issue 1: Can't get correct recipe ID and no data shows when clicking the favorite button
Root Causes:
- Route parameter mismatch: Your Link uses
to={/recipe-details/recipe-number:${recipe._id}}which creates a URL like/recipe-details/recipe-number:123. If your route definition isn't set up to capture the ID afterrecipe-number:,useParams()won't pick uprecipeIdcorrectly. - Redundant & risky data fetch: You already receive the full
recipeobject as a prop, but you're trying to re-fetch it viauserService.getOne(recipeId)—this introduces unnecessary async delay, and if the ID is wrong, you'll get no data at all. - Unsafe state access: When clicking the favorite button immediately, the
recipecstate might still beundefined(since the useEffect fetch is asynchronous), leading to errors when accessing properties likerecipec._id.
Fixes:
Simplify by using the existing recipe prop directly, fix the route link format, and add error handling:
import "./RecipeModel.css"; import { Link, useNavigate } from 'react-router-dom'; import React, { useContext } from 'react'; import * as userService from '../../../services/userService'; import { AuthContext } from "../../../contexts/AuthContext"; const RecipeModel = ({ recipe }) => { const history = useNavigate(); const { user } = useContext(AuthContext); const HandleFavourite = (e) => { e.preventDefault(); // Use the passed recipe prop directly instead of fetching again console.log(`Recipe ${recipe._id}`); const { name, time, imageUrl, ingredients, instructions } = recipe; userService.addFavourite({ name, time, imageUrl, ingredients, instructions }, user.accessToken) .then(result => { console.log(result); history('/favourites'); }) .catch(err => console.error("Failed to add favorite:", err)); // Add error logging } return ( <article className="articles"> <img className="img2" src={recipe.imageUrl} alt={recipe.name}/> <h1>{recipe.name}</h1> <p className="cut-text">{recipe.instructions}</p> <div className="btns1"> {/* Fix route link to match your route definition (e.g., /recipe-details/:recipeId) */} <Link smooth="true" className="btnd" to={`/recipe-details/${recipe._id}`}>Details</Link> <button className="favour" onClick={HandleFavourite} ><i className="fas fa-solid fa-heart-circle-plus"></i></button> </div> </article> ); }; export default RecipeModel;
Issue 2: Favorite page shows only HTML template, no actual recipe data
Root Causes:
- Missing authentication in fetch: Your
getAllFavouritesfunction doesn't send an auth token, which might result in an empty or unauthorized response from your backend. - Unvalidated data structure: You're converting the API response to
Object.values(recipes)without checking if the response is an array or object, which could lead to invalid data. - No error handling or loading state: If the fetch fails, you won't know, and empty state leaves users confused.
Fixes:
Add auth to the fetch, validate data, and add safety checks in components:
Updated Favourites Component:
import "./Favourites.css"; import { useContext, useState, useEffect } from 'react'; import RecipeFavourite from "../Recipe/RecipeFavourite/RecipeFavourite"; import * as userService from '../../services/userService'; import { AuthContext } from "../../contexts/AuthContext"; function Favourites() { const [favourites, setFavourites] = useState([]); const [isLoading, setIsLoading] = useState(true); const { user } = useContext(AuthContext); useEffect(() => { userService.getAllFavourites(user.accessToken) .then(result => { console.log("Favorites data received:", result); // Log to verify structure // Ensure we're working with an array const validFavourites = Array.isArray(result) ? result : Object.values(result); setFavourites(validFavourites); }) .catch(err => { console.error("Failed to fetch favorites:", err); setFavourites([]); }) .finally(() => setIsLoading(false)); },[user.accessToken]); // ... existing scroll logic ... return ( <div> <h1 className="h1m">Your Favourite Recipes</h1> <section className="secfav"> {isLoading ? ( <p>Loading your favorite recipes...</p> ) : favourites.length === 0 ? ( <p>No favorite recipes yet! Add some to get started.</p> ) : ( favourites.map(x => <RecipeFavourite key={x._id} recipe={x} />) )} </section> </div> ); } export default Favourites;
Updated getAllFavourites in userService:
export const getAllFavourites = async (token) => { let response = await fetch(`${baseUrl}/favourites`, { headers: { 'X-Authorization': token, // Add auth header required by your backend } }); if (!response.ok) throw new Error(`Fetch failed: ${response.statusText}`); let recipes = await response.json(); return recipes; }
Safe RecipeFavourite Component (add optional chaining):
import "./RecipeFavourite.css"; import { Link } from 'react-router-dom'; const RecipeFavourite = ({ recipe }) => { return ( <article className="articles"> {/* Use placeholder if image URL is missing */} <img className="img4" src={recipe?.imageUrl || "/placeholder-recipe.jpg"} alt={recipe?.name || "Recipe image"}/> <h1>{recipe?.name || "Unnamed Recipe"}</h1> <p className="cut-text">{recipe?.instructions || "No instructions available"}</p> <div className="btns1"> <Link smooth="true" className="btnd" to={`/recipe-details/${recipe?._id}`}>Details</Link> <button className="favnon"><i className="fas fa-solid fa-heart-circle-minus"></i></button> </div> </article> ); }; export default RecipeFavourite;
内容的提问来源于stack exchange,提问作者DimitriTfb
相关产品推荐
相关产品推荐

