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

如何正确在FlatList中展示商品?解决布局报错与闪退问题

问题分析与修复方案

1. 先解决FlatList样式报错

FlatList继承自ScrollView,alignItems这类控制子元素布局的样式必须通过contentContainerStyle传递,不能放在style属性里——这是React Native的硬性规则。但你之前移动后闪退的核心原因不是这个,是其他代码bug,先把样式问题修正:

把原styles里的container拆分,将布局和非布局样式分开:

const styles = StyleSheet.create({
  // FlatList自身样式(非布局相关)
  flatListWrapper: {
    marginTop: 1,
  },
  // FlatList内容容器的布局样式
  flatListContent: {
    alignItems: "flex-start",
  },
  // 其他样式保持不变
  containerProduct: {
    padding: 3,
    flex: 1, // 新增:让每个商品容器平分两列宽度
  },
  // ...剩余样式代码
});

然后在FlatList组件中对应配置:

<FlatList
  style={styles.flatListWrapper}
  contentContainerStyle={styles.flatListContent}
  // 其他属性保持不变
/>

2. 修复导致闪退的核心bug:renderItem参数错误

FlatList的renderItem回调接收的参数是包含item字段的对象,你写成了({product}),导致product为undefined,访问product.id时直接触发致命错误,引发闪退。修改如下:

const renderItem = ({item}) => { // 把product改成item
  const product = item; // 可以保留product变量,也直接用item
  return (
    <TouchableWithoutFeedback
      onPress={() => gotoProduct(product.id)}
      // key放在FlatList的keyExtractor里即可,不需要在这里加
    >
      {/* 内部代码保持不变 */}
    </TouchableWithoutFeedback>
  );
};

3. 修复加载更多的逻辑问题

当前代码每次加载会替换原有商品列表,且limit不断增大,不符合需求:

  • 应该追加新商品到现有列表,而非替换
  • limit固定为10即可,只需要递增start值

修改getProducts和handleLoadMore:

const getProducts = async () => {
  try {
    const response = await getLastProductsApi(
      startLimit.start,
      startLimit.limit
    );
    // 追加新商品,保留原有数据
    setProducts(prev => [...prev, ...response]);
  } catch (err) {
    console.error('加载商品失败:', err);
  } finally {
    // 请求完成后关闭加载状态
    setisLoading(false);
  }
};

const handleLoadMore = () => {
  // 避免重复触发加载
  if (isLoading) return;
  setStartLimit(prev => ({
    start: prev.start + 10,
    limit: 10, // 固定每页10条
  }));
  setisLoading(true);
};

4. 优化keyExtractor

尽量用商品id作为key,而非index,提升列表性能:

keyExtractor={(item) => item.id.toString()}

完整修复后的NewProducts组件代码

import React, { useState, useEffect } from 'react';
import { View, Text, Image, FlatList, TouchableWithoutFeedback, StyleSheet } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import Loading from './Loading';
import { getLastProductsApi } from './api';

export default function NewProducts() {
  const [products, setProducts] = useState([]);
  const [isLoading, setisLoading] = useState(false);
  const [startLimit, setStartLimit] = useState({ start: 0, limit: 10 });
  const navigation = useNavigation();

  useEffect(() => {
    setisLoading(true);
    getProducts();
  }, [startLimit]);

  const gotoProduct = (id) => {
    navigation.push("product", { idProduct: id });
  };

  const getProducts = async () => {
    try {
      const response = await getLastProductsApi(
        startLimit.start,
        startLimit.limit
      );
      setProducts(prevProducts => [...prevProducts, ...response]);
    } catch (error) {
      console.error('加载商品失败:', error);
    } finally {
      setisLoading(false);
    }
  };

  const renderItem = ({item}) => {
    const product = item;
    return (
      <TouchableWithoutFeedback
        onPress={() => gotoProduct(product.id)}
      >
        <View style={styles.containerProduct}>
          <View style={styles.product}>
            <Image
              style={styles.image}
              source={{
                uri: `${product.attributes.images.data[0].attributes.formats.small.url}`,
              }}
            />
            <Text style={styles.name} numberOfLines={1} ellipsizeMode="tail">
              {product.attributes.title}
            </Text>
          </View>
        </View>
      </TouchableWithoutFeedback>
    );
  };

  const renderFooter = () => {
    return isLoading && <Loading text="加载更多商品..." size="medium" />;
  };

  const handleLoadMore = () => {
    if (isLoading) return;
    setStartLimit(prev => ({
      start: prev.start + 10,
      limit: 10,
    }));
    setisLoading(true);
  };

  return (
    <FlatList
      style={styles.flatListWrapper}
      contentContainerStyle={styles.flatListContent}
      numColumns={2}
      data={products}
      renderItem={renderItem}
      keyExtractor={(item) => item.id.toString()}
      ListFooterComponent={renderFooter}
      onEndReached={handleLoadMore}
      onEndReachedThreshold={0.1} // 调整阈值,避免过早/过晚触发加载
    />
  );
}

const styles = StyleSheet.create({
  flatListWrapper: {
    marginTop: 1,
  },
  flatListContent: {
    alignItems: "flex-start",
  },
  containerProduct: {
    padding: 3,
    flex: 1,
  },
  product: {
    padding: 10,
    backgroundColor: "#f0f0f0",
    borderRadius: 20,
  },
  image: {
    height: 150,
    resizeMode: "contain",
    borderRadius: 20,
  },
  name: {
    marginTop: 15,
    fontSize: 15,
  },
});

内容的提问来源于stack exchange,提问作者Ander Canales Medina

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 22:20:41