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

React组件中useEffect未被favoriteId prop触发的问题排查

为什么点击点赞后useEffect没有触发?

核心问题分析

你的代码存在三个关键错误,导致useEffect无法触发:

  1. 父组件传递favoriteId时的错误
    父组件的FlatList渲染RecipeCard时,错误地传递了临时变量favoriteId,而非每个食谱项自带的item.favoriteId。这导致所有卡片拿到的是同一个值,且初始加载后该变量不会更新。

  2. 子组件直接修改props的favoriteId
    在onLikePress中直接给favoriteId赋值(favoriteId = null或favoriteId = res.id)是React的禁忌:props是只读的,直接修改不会触发组件重渲染,也无法同步到父组件状态,因此useEffect依赖的favoriteId根本没有变化。

  3. 点赞操作后未更新父组件状态
    你只修改了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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 11:51:32