React组件中useEffect未被favoriteId prop触发的问题排查
为什么点击点赞后useEffect没有触发?
核心问题分析
你的代码存在三个关键错误,导致useEffect无法触发:
父组件传递
favoriteId时的错误
父组件的FlatList渲染RecipeCard时,错误地传递了临时变量favoriteId,而非每个食谱项自带的item.favoriteId。这导致所有卡片拿到的是同一个值,且初始加载后该变量不会更新。子组件直接修改props的
favoriteId
在onLikePress中直接给favoriteId赋值(favoriteId = null或favoriteId = res.id)是React的禁忌:props是只读的,直接修改不会触发组件重渲染,也无法同步到父组件状态,因此useEffect依赖的favoriteId根本没有变化。点赞操作后未更新父组件状态
你只修改了Firestore的数据,但没有更新父组件的recipes数组,子组件接收的favoriteIdprops始终不变,useEffect自然不会触发。
修复后的代码
父组件修改
export const HomeScreen = () => { const { user } = useContext(AuthenticatedUserContext); const [recipes, setRecipes] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { if (user) getRecipes(); }, [user]); const getRecipes = async () => { const querySnapshot = await getDocs(collection(db, "receipes")); const fetchedRecipes = []; for (const d of querySnapshot.docs) { const citiesRef = collection(db, "favorites"); const q = query( citiesRef, where("userId", "==", user.uid), where("recipeId", "==", d.id) ); const favSnapshot = await getDocs(q); const favoriteId = favSnapshot.empty ? null : favSnapshot.docs[0].id; fetchedRecipes.push({ ...d.data(), id: d.id, favoriteId, }); } setRecipes(fetchedRecipes); setLoading(false); }; // 子组件回调:更新单个食谱的收藏状态 const updateRecipeFavorite = (recipeId, newFavoriteId) => { setRecipes(prev => prev.map(recipe => recipe.id === recipeId ? {...recipe, favoriteId: newFavoriteId} : recipe ) ); }; return ( <View style={styles.container}> <Text style={{ fontSize: 24, fontWeight: "bold", paddingBottom: 10 }}> Recepten </Text> {!loading && recipes.length > 0 && ( <FlatList data={recipes} renderItem={({ item }) => ( <RecipeCard name={item.title} id={item.id} image={item.thumbnail} favoriteId={item.favoriteId} // 传递当前项的favoriteId updateFavorite={updateRecipeFavorite} // 传递更新回调 /> )} keyExtractor={(item) => item.id} horizontal /> )} </View> ); };
子组件修改
const RecipeCard = ({ name, image, id, favoriteId, updateFavorite }) => { const { user } = useContext(AuthenticatedUserContext); const [showNoAccountModal, setShowNoAccountModal] = useState(false); // 直接由props计算收藏状态,无需额外维护state const isFavorite = !!favoriteId; const onLikePress = async () => { if (favoriteId) { await deleteDoc(doc(db, "favorites", favoriteId)); updateFavorite(id, null); // 通知父组件取消收藏 } else { const res = await addDoc(collection(db, "favorites"), { userId: user.uid, recipeId: id, }); updateFavorite(id, res.id); // 通知父组件添加收藏 } }; return ( <TouchableWithoutFeedback style={{ flex: 1, padding: 10 }}> <View> <AntDesign onPress={() => { if (!user) { setShowNoAccountModal(true); } else { onLikePress(); } }} name={isFavorite ? "like1" : "like2"} color="black" size={30} /> {/* 这里可以添加showNoAccountModal的弹窗实现 */} </View> </TouchableWithoutFeedback> ); }; export default RecipeCard;
关键修复点
- 父组件正确传递每个食谱项的
favoriteId,而非全局临时变量 - 通过回调函数实现子组件到父组件的状态同步,保证props更新触发重渲染
- 遵循React单向数据流,禁止直接修改props
- 简化子组件逻辑,直接由props计算收藏状态,去掉不必要的
useEffect
内容的提问来源于stack exchange,提问作者Bowis
相关产品推荐
相关产品推荐

