You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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:

  1. 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 after recipe-number:, useParams() won't pick up recipeId correctly.
  2. Redundant & risky data fetch: You already receive the full recipe object as a prop, but you're trying to re-fetch it via userService.getOne(recipeId)—this introduces unnecessary async delay, and if the ID is wrong, you'll get no data at all.
  3. Unsafe state access: When clicking the favorite button immediately, the recipec state might still be undefined (since the useEffect fetch is asynchronous), leading to errors when accessing properties like recipec._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:

  1. Missing authentication in fetch: Your getAllFavourites function doesn't send an auth token, which might result in an empty or unauthorized response from your backend.
  2. 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.
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 20:47:38