React菜谱收藏功能异常求助:无法获取收藏列表并校验重复
问题
开发React菜谱收藏功能,需求是仅当目标菜谱未在用户收藏列表中时,才添加至收藏。目前调用接口获取favourites数组并通过ID校验重复的功能失效,无法获取到收藏数组,console.log输出为空。
RecipeModel组件逻辑
import "./RecipeModel.css"; import { Link, NavLink, useNavigate, useParams } from 'react-router-dom'; import React, { useLayoutEffect, useState,useEffect, useContext } from 'react'; import * as userService from '../../../services/userService'; import { AuthContext } from "../../../contexts/AuthContext"; import * as likeService from '../../../services/likeService'; import useRecipeState from "../../EditRecipe/RecipeState"; const RecipeModel = ({recipe}) => { const history = useNavigate(); const {user} = useContext(AuthContext); const {recipeId} = useParams(); const [likes,setLikes] = useRecipeState(recipeId); const [favourites,setFavourites] = useState([]); useEffect(() => { likeService.getLikes(recipe._id) .then(likes => { setLikes(state => ({...state, likes})) }) }, []); useEffect(() => { userService.yourFavourites(user._id) .then(result => { setFavourites(result); }) },[]); const valid = !(favourites.some(i => i._id === recipe._id)); if(valid) { const HandleFavourite = (e) => { e.preventDefault(); console.log(`Recipe ${recipe._id}`); const name = recipe.name; const time = recipe.time; const imageUrl = recipe.imageUrl; const ingredients = recipe.ingredients; const instructions = recipe.instructions; userService.addFavourite({ name, time, imageUrl, ingredients, instructions },user.accessToken) .then(result => { console.log(result); console.log(index); console.log(user._id); history('/favourites'); }) } } const likeButtonClick = () => { if (user._id === recipe._ownerId) { return; } if (recipel.likes.includes(user._id)) { return; } likeService.like(user._id, recipeId) .then(() => { setLikes(state => ({...state, likes: [...state.likes, user._id]})); console.log('liked'); }); }; return ( <article className="articles"> <img className="img2" src={recipe.imageUrl}/> <h1>{recipe.name}</h1> <p className="cut-text">{recipe.instructions}</p> <div className="btns1"> <Link smooth= "true" className="btnd" to={`/recipe-details/recipe-number:${recipe._id}`}>Details</Link> <button className="like" onClick={likeButtonClick} > <i className="fas fa-solid fa-thumbs-up"></i> {likes.likes?.length || 0}</button> <button className="favour" onClick={HandleFavourite} ><i className="fas fa-solid fa-heart-circle-plus"></i></button> </div> </article> ); }; export default RecipeModel;
userService相关逻辑
export const addFavourite = async (recipeData,token) => { let response = await fetch(`${baseUrl}/favourites`, { method: 'POST', headers: { 'content-type': 'application/json', 'X-Authorization': token, }, body: JSON.stringify(recipeData) }); let result = await response.json(); return result; }; export const getAllFavourites = async () => { let response = await fetch(`${baseUrl}/favourites`) let recipes = await response.json(); let result = Object.values(recipes); return result; } export const removeFavourite = (recipeId, token) => { return fetch(`${baseUrl}/favourites/${recipeId}`, { method: 'DELETE', headers: { 'X-Authorization': token, }, }).then(res => res.json()); }; export const getOneFav = (recipeId) => { return fetch(`${baseUrl}/favourites/${recipeId}`) .then(res => res.json()) }; export const yourFavourites = (ownerId) => { let query = encodeURIComponent(`_ownerId="${ownerId}"`); return request.get(`${baseUrl}/favourites?where=${query}`); };
问题排查与解决方案
一、核心问题定位
- 接口调用失败:
yourFavourites函数中使用未定义的request.get,且未携带用户认证令牌,后端大概率拒绝返回私有数据。 - 组件逻辑错误:
HandleFavourite定义在if(valid)块内,当菜谱已收藏时函数不存在,点击按钮直接报错;初始favourites为空,异步数据返回后校验逻辑未更新。 - 依赖缺失:获取收藏列表的
useEffect未添加user依赖,用户切换账号后无法重新请求数据;点赞函数存在笔误recipel.likes。
二、修复方案
1. 修复userService中的yourFavourites函数
替换未定义的request.get为标准fetch,并携带认证令牌:
export const yourFavourites = async (ownerId, token) => { let query = encodeURIComponent(`_ownerId="${ownerId}"`); const response = await fetch(`${baseUrl}/favourites?where=${query}`, { headers: { 'X-Authorization': token, } }); return response.json(); };
2. 修正RecipeModel组件逻辑
import "./RecipeModel.css"; import { Link, useNavigate, useParams } from 'react-router-dom'; import React, { useState, useEffect, useContext } from 'react'; import * as userService from '../../../services/userService'; import { AuthContext } from "../../../contexts/AuthContext"; import * as likeService from '../../../services/likeService'; import useRecipeState from "../../EditRecipe/RecipeState"; const RecipeModel = ({recipe}) => { const history = useNavigate(); const {user} = useContext(AuthContext); const {recipeId} = useParams(); const [likes,setLikes] = useRecipeState(recipeId); const [favourites,setFavourites] = useState([]); useEffect(() => { likeService.getLikes(recipe._id) .then(likes => { setLikes(state => ({...state, likes})) }) }, [recipe._id]); // 修复:添加user依赖,携带token请求,捕获错误 useEffect(() => { if (!user?._id || !user?.accessToken) return; userService.yourFavourites(user._id, user.accessToken) .then(result => { setFavourites(result); console.log('收藏列表:', result); }) .catch(err => console.error('获取收藏列表失败:', err)); }, [user]); // 修复:直接定义函数,点击时再做校验 const HandleFavourite = (e) => { e.preventDefault(); const isAlreadyFavourite = favourites.some(i => i._id === recipe._id); if (isAlreadyFavourite) return; const recipeData = { name: recipe.name, time: recipe.time, imageUrl: recipe.imageUrl, ingredients: recipe.ingredients, instructions: recipe.instructions }; userService.addFavourite(recipeData, user.accessToken) .then(result => { console.log('添加收藏成功:', result); // 本地更新收藏列表,避免刷新页面 setFavourites(prev => [...prev, result]); history('/favourites'); }) .catch(err => console.error('添加收藏失败:', err)); }; const likeButtonClick = () => { if (!user?._id) return; if (user._id === recipe._ownerId) return; // 修复笔误:recipel -> recipe if (recipe.likes?.includes(user._id)) return; likeService.like(user._id, recipeId) .then(() => { setLikes(state => ({...state, likes: [...state.likes, user._id]})); console.log('点赞成功'); }) .catch(err => console.error('点赞失败:', err)); }; // 计算收藏状态,用于按钮交互 const isFavourite = favourites.some(i => i._id === recipe._id); return ( <article className="articles"> <img className="img2" src={recipe.imageUrl}/> <h1>{recipe.name}</h1> <p className="cut-text">{recipe.instructions}</p> <div className="btns1"> <Link smooth="true" className="btnd" to={`/recipe-details/recipe-number:${recipe._id}`}>Details</Link> <button className="like" onClick={likeButtonClick} > <i className="fas fa-solid fa-thumbs-up"></i> {likes.likes?.length || 0} </button> {/* 根据收藏状态修改按钮状态 */} <button className={`favour ${isFavourite ? 'active' : ''}`} onClick={HandleFavourite} disabled={isFavourite} > <i className="fas fa-solid fa-heart-circle-plus"></i> {isFavourite ? '已收藏' : '收藏'} </button> </div> </article> ); }; export default RecipeModel;
3. 额外优化建议
- 接口调用添加错误捕获,避免静默失败;
- 添加收藏列表加载状态,提升用户体验;
- 收藏按钮根据状态禁用或修改样式,防止重复点击。
内容的提问来源于stack exchange,提问作者DimitriTfb
相关产品推荐
相关产品推荐

