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

React Native中FlatList加载新项时添加按钮卡顿问题

问题描述
  • 需求:实现商品列表,点击「添加至购物篮」按钮后,按钮切换为数量计数器供用户选择商品数量。
  • 问题:向下滚动FlatList加载新项时,绿色添加按钮会卡顿1秒左右才切换为计数器。

商品列表添加按钮界面

相关代码

FlatList代码

<FlatList
  data={products}
  renderItem={renderPrices}
  numColumns={1}
  keyExtractor={(item, index) => index.toString()}
  refreshControl={
    <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
  }
  ListFooterComponent={() =>
    loading ? (
      <View style={styles.spinnerContainer}>
        <Spinner size="giant" />
      </View>
    ) : !productStore.moreItems ? (
      <View style={{ justifyContent: 'center', alignItems: 'center', paddingVertical: 20, backgroundColor: Colors.secondary500, marginHorizontal: 3 }}>
        {IconComponent(evaIcons.done, Colors.black900, { width: 30, height: 30 })}
        <Text>سلات قائمة ديل منتجات</Text>
      </View>
    ) : null
  }
  nestedScrollEnabled={true}
  onEndReached={() => getProductList()}
  onEndReachedThreshold={0.5}
/>

商品列表项组件代码

return (
  <Layout style={styles.container}>
    <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
      <Image
        source={{
          uri: image
        }}
        resizeMode="contain"
        style={{ width: 100, height: 70 }}
      />
      <View style={styles.priceContainer}>
        <Text style={styles.priceText}>{price.price}</Text>
        <Text style={styles.currencyText}>دم</Text>
      </View>
      <PriceType
        quantityType={
          price.sellUnit === SellUnitEnum.ATOMIC
            ? price.atomicRtlName
            : price.packingRtlName
        }
      />
    </View>
    <View style={{ flex: 3 }}>
      <View
        style={{
          flex: 1,
          flexDirection: "row-reverse",
          justifyContent: "space-between",
          alignItems: "center",
        }}
      >
        <Text
          style={{
            width: "75%",
            textAlign: "right",
            fontFamily: "almarai-bold",
            marginRight: 3,
          }}
        >
          {price.rtlName}
        </Text>
        <AwalPoint points={price.points} backgroundColor={Colors.gray200} textColor={Colors.gray800} />
      </View>
      <View style={{ flex: 1, flexDirection: "row-reverse" }}>
        <PackageQuantity
          title={price.atomicRtlName.toString() + " في"}
          packageName={price.packingRtlName}
          quantity={price.packageQuantity.toString()}
          image={require("../assets/surprise.png")}
          backgroundColor={Colors.primary400}
          containerStyle={{ flex: 1 }}
        />
        <PackageQuantity
          title="تمن"
          packageName={
            price.sellUnit === "ATOMIC"
              ? price.packingRtlName
              : price.atomicRtlName
          }
          quantity={
            (price.sellUnit === "ATOMIC"
              ? (price.price * price.packageQuantity).toFixed(2)
              : (price.price / price.packageQuantity).toFixed(2)
            ).toString() + " دم"
          }
          image={require("../assets/dollar.png")}
          backgroundColor={Colors.secondary400}
          containerStyle={{ flex: 1.5 }}
        />
      </View>
      <View style={{ flex: 1, flexDirection: "row", paddingBottom: 7 }}>
        {basketItem === undefined && !isFocused ? (
          <Button
            style={{
              height: 45,
              borderWidth: 0,
              borderRadius: 100,
              backgroundColor: Colors.secondary500,
            }}
            onPress={() => {
              console.log("button clicked: ");
              useDispatch(updateItems({ item: price, quantity: 1, error: undefined, productImage: image }));
            }}
            accessoryRight={IconComponent(evaIcons.cart, Colors.white500)}
          >
            {(props) => (
              <Text
                {...props}
                style={{
                  fontFamily: "almarai-bold",
                  fontSize: 15,
                  color: Colors.white500,
                }}
              >
                أضف إلى السلة
              </Text>
            )}
          </Button>
        ) : (
          <CounterInput
            style={{ flex: 1 }}
            priceId={price.id}
            isFocused={isFocused}
            setIsFocused={setIsFocused}
            maxValue={price.storeQuantity}
            buttonStyle={{ backgroundColor: Colors.primary500, borderWidth: 0 }}
          />
        )}
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'flex-end' }}>
          {(price.isDiscountable === true) ?
            <View style={{ flexDirection: 'row-reverse', justifyContent: 'center', alignItems: 'center' }}>
              <Image
                source={require("../assets/promo.png")}
                resizeMode="contain"
                style={{ width: 30, height: 30 }}
              />
              <Text style={{ textAlign: 'center', fontFamily: 'almarai-regular' }}>فيه روميز</Text>
            </View> : null}
        </View>
      </View>
    </View>
  </Layout>
);
解决方案建议
  1. 替换keyExtractor:不要用index作为列表项的key,改用商品的唯一标识(比如price.id),避免FlatList复用组件时因index变化导致状态错乱:
    keyExtractor={(item) => item.id.toString()}
    
  2. 优化组件重渲染:用React.memo包裹商品列表项组件,减少不必要的重渲染:
    const ProductItem = React.memo(({ price, image, basketItem, isFocused, setIsFocused }) => {
      // 组件原有代码
    });
    
  3. 状态判断优化:确保basketItem的判断基于稳定的数据源(比如从Redux直接根据price.id查询),避免依赖延迟更新的props。
  4. FlatList性能配置:添加以下属性提升滚动和加载性能:
    removeClippedSubviews={true} // 移除不可见区域的组件
    windowSize={5} // 控制预渲染的列表项数量,减少内存占用
    maxToRenderPerBatch={10} // 每次批量渲染的项数
    
  5. 提取ListFooterComponent:不要在render中动态创建Footer组件,抽成单独的组件避免重复创建:
    const ListFooter = () => {
      // 原有Footer逻辑
    };
    // 然后在FlatList中使用
    ListFooterComponent={<ListFooter />}
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 01:20:24