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

嵌套FlatList刷新后条目未更新的React Native技术问题

问题描述

我有一个包含多分区的FlatList,部分分区带有嵌套条目,这类分区使用横向FlatList展示条目,部分条目会出现在多个分区中。

点赞/收藏某个条目后执行刷新操作,该条目在其他分区中并未更新,但在<LargeVenue/>组件和重启应用后会正常更新,说明功能本身有效,推测是React Native代码存在问题。


renderItem 代码

const renderItem = useCallback(({ item }) => {
  switch (item.size) {
    case "small":
      return (
        <SmallSection
          title={item.title}
          items={item.items}
          footer={item.footer}
        />
      );
    case "medium":
      return (
        <MediumSection
          title={item.title}
          items={item.items}
        />
      );
    default:
      return (
        <LargeVenue
          venue={item}
          favouriteVenue={() => favouriteVenue(item, !item.isFavourite)}
          pushVenueScreen={() => pushVenueScreen(item)}
        />
      );
    }
  }, []);

MediumSection 代码

export default function MediumSection({ title, items }) {
  const [venues, setVenues] = useState(items);
  const { user } = useAuth();
  const { language } = useLanguage();
  const navigation = useNavigation();

  const renderItem = useCallback(
    ({ item }) => {
      const updateFavourite = (isFavourite) => {
        updateFavouritesBatch(user.uid, item, !isFavourite);
        const updatedItem = { ...item, isFavourite };
        setVenues((prevState) => {
          const updatedItems = prevState.map((currentItem) => {
            if (currentItem.id === updatedItem.id) {
              return updatedItem;
            } else {
              return currentItem;
            }
          });
          return updatedItems;
        });
      };

      if (items.length === 1) {
        return (
          <SingleVenue
            venue={item}
            pushVenueScreen={() => pushVenueScreen(item)}
            favouriteVenue={() => updateFavourite(!item.isFavourite)}
          />
        );
      } else {
        return (
          <MediumVenue
            venue={item}
            pushVenueScreen={() => pushVenueScreen(item)}
            favouriteVenue={() => updateFavourite(!item.isFavourite)}
          />
        );
      }
    },
    [venues]
  );

  const itemSeparatorComponent = () => {
    return <View style={{ width: 10 }} />;
  };

  const pushVenueScreen = (venue) => {
    navigation.navigate("VenueLayout", {
      screen: "Venue",
      params: { id: venue.id, isFavourite: venue.isFavourite },
    });
  };

  return (
    <>
      <TouchableOpacity
        style={styles.headingContainer}
        disabled={items.length === 1}>
        <Heading
          style={styles.heading}
          numberOfLines={2}>
          {title[language]}
        </Heading>
        {items.length > 1 && <Body>All</Body>}
      </TouchableOpacity>
      <FlatList
        keyExtractor={(item) => item.id}
        data={venues}
        renderItem={renderItem}
        horizontal
        contentContainerStyle={styles.contentContainerStyle}
        ItemSeparatorComponent={itemSeparatorComponent}
        showsHorizontalScrollIndicator={false}
        scrollEnabled={items.length > 1}
      />
    </>
  );
}

onRefresh 代码

const onRefresh = useCallback(() => {
  setRefreshing(true); // setRefreshing(false) within getData completion.
  getData();
}, []);

问题原因与修复方案

核心问题

  1. 子组件状态隔离:MediumSection将传入的items存入自身useState,刷新时父组件数据源更新,但子组件内部的venues状态未同步,导致其他分区条目未更新。
  2. 依赖数组错误:父组件renderItem的useCallback依赖为空,数据源更新时不会重新生成渲染函数;MediumSection的renderItem依赖venues而非外部必要变量,导致状态不同步。
  3. 分散式状态更新:子组件单独维护点赞状态,未同步到父组件全局数据源,导致其他分区的同一条目无法感知变化。

修复步骤

1. 移除MediumSection内部状态,统一由父组件管理

删除MediumSection中的venues状态,直接使用props传入的items,并通过回调通知父组件更新全局数据源:

export default function MediumSection({ title, items, onUpdateFavourite }) {
  const { user } = useAuth();
  const { language } = useLanguage();
  const navigation = useNavigation();

  const renderItem = useCallback(
    ({ item }) => {
      const updateFavourite = () => {
        const newIsFavourite = !item.isFavourite;
        updateFavouritesBatch(user.uid, item, newIsFavourite);
        // 通知父组件更新全局数据源
        onUpdateFavourite(item.id, newIsFavourite);
      };

      if (items.length === 1) {
        return (
          <SingleVenue
            venue={item}
            pushVenueScreen={() => pushVenueScreen(item)}
            favouriteVenue={updateFavourite}
          />
        );
      } else {
        return (
          <MediumVenue
            venue={item}
            pushVenueScreen={() => pushVenueScreen(item)}
            favouriteVenue={updateFavourite}
          />
        );
      }
    },
    [items, user, navigation] // 修正依赖数组
  );

  // ...其余代码不变,FlatList的data替换为items
  <FlatList
    keyExtractor={(item) => item.id}
    data={items}
    // ...其余属性保持不变
  />
}

2. 父组件统一维护数据源并处理更新

在父组件中管理全局分区数据,收到子组件的更新通知后,同步更新所有分区中的对应条目:

// 父组件核心代码
const [sections, setSections] = useState([]);

// 获取数据函数
const getData = async () => {
  const fetchedSections = await fetchYourData();
  setSections(fetchedSections);
  setRefreshing(false);
};

// 处理点赞状态更新
const handleUpdateFavourite = (venueId, isFavourite) => {
  setSections(prevSections => 
    prevSections.map(section => {
      // 更新带items的分区
      if (section.items) {
        return {
          ...section,
          items: section.items.map(item => 
            item.id === venueId ? { ...item, isFavourite } : item
          )
        };
      }
      // 更新LargeVenue类型的分区
      return section.id === venueId ? { ...section, isFavourite } : section;
    })
  );
};

// 更新renderItem的依赖与props
const renderItem = useCallback(({ item }) => {
  switch (item.size) {
    case "small":
      return (
        <SmallSection
          title={item.title}
          items={item.items}
          footer={item.footer}
          onUpdateFavourite={handleUpdateFavourite}
        />
      );
    case "medium":
      return (
        <MediumSection
          title={item.title}
          items={item.items}
          onUpdateFavourite={handleUpdateFavourite}
        />
      );
    default:
      return (
        <LargeVenue
          venue={item}
          favouriteVenue={() => handleUpdateFavourite(item.id, !item.isFavourite)}
          pushVenueScreen={() => pushVenueScreen(item)}
        />
      );
    }
  }, [sections, handleUpdateFavourite]); // 添加正确依赖

3. 修正onRefresh的依赖数组

确保onRefresh依赖getData,保证刷新时调用最新的数据获取逻辑:

const onRefresh = useCallback(() => {
  setRefreshing(true);
  getData();
}, [getData]);

修复逻辑说明

  • 统一数据源:所有分区的条目都从父组件全局状态获取,更新时所有引用该条目的分区同步刷新。
  • 移除子组件内部状态:避免子组件与父组件数据源不一致,刷新时父组件数据更新后,子组件自动获取最新items。
  • 修正依赖:确保useCallback包裹的函数在依赖变化时重新生成,保证组件拿到最新参数与状态。

内容的提问来源于stack exchange,提问作者David Henry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 08:37:02