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

React Native FlatList子组件Props丢失:点击卡片显示错误数据

解决React Native FlatList点击卡片显示错误RBSheet数据的问题

我来帮你分析下问题所在,以及给出两种可行的解决方案:

问题根源

你现在遇到的问题主要有两个核心原因:

  1. 组件复用导致Props未更新:你的FlatList使用item.nombre作为keyExtractor,如果存在名称相同的item,FlatList会复用之前的组件实例,导致新的props没有正确传递,点击时显示旧数据。
  2. 多模态框的Ref冲突:每个Entry组件都内置了一个RBSheet,虽然类组件的实例this是独立的,但一旦FlatList复用组件,旧的RBSheet ref会被新组件覆盖,最终点击任何卡片都会触发最后一个渲染的模态框。

解决方案一:将RBSheet统一移到父组件(推荐)

这种方案更高效,所有卡片共用一个模态框,避免重复渲染多个RBSheet,同时能精准控制显示的数据:

1. 修改父组件(FlatList所在组件)

添加状态管理选中的item和模态框显示状态,同时把RBSheet移到父组件:

class YourParentComponent extends React.Component {
  state = {
    data: [],
    selectedItem: null,
    isModalVisible: false
  };

  // 处理卡片点击,保存选中的item并打开模态框
  handleCardClick = (item) => {
    this.setState({ selectedItem: item }, () => {
      this.rbsheetRef.open();
    });
  };

  render() {
    return (
      <View style={{ flex: 1, justifyContent: "center", textAlign: "center", alignItems: "center", marginTop: "5%" }} >
        <FlatList 
          style={{ width: "100%", alignContent: "center", backgroundColor: "#000" }} 
          data={this.state.data} 
          showsVerticalScrollIndicator={true} 
          // 改用唯一标识符作为key,优先用item.id(如果存在)
          keyExtractor={item => item.id || item.nombre} 
          renderItem={({ item }) => (
            <Entry item={item} onPress={() => this.handleCardClick(item)} />
          )} 
        />

        {/* 统一的RBSheet */}
        <RBSheet 
          ref={ref => this.rbsheetRef = ref}
          animationType="slide" 
          closeOnPressMask={true} 
          height={RF(80)} 
          duration={100} 
          customStyles={{ 
            container: { 
              justifyContent: "center", 
              alignItems: "center", 
              textAlign: "center", 
              alignContent: "center", 
              borderTopLeftRadius: 20, 
              borderTopRightRadius: 20 
            } 
          }} 
        >
          {/* 只有选中item存在时才渲染内容 */}
          {this.state.selectedItem && (
            <Text style={{ color: "#484855", fontSize: RF(3.5), paddingTop: 25, textAlign: "center" }} > 
              {this.state.selectedItem.nombre} 
            </Text>
          )}
        </RBSheet>
      </View>
    );
  }
}

2. 修改Entry组件

移除内部的RBSheet,只保留卡片UI,并接收父组件传递的点击回调:

// Entry.js
class Entry extends React.Component {
  render() {
    const { item, onPress } = this.props;
    return ( 
      <View style={{ flexDirection: "row", alignContent: "center", alignItems: "center", textAlign: "center", marginBottom: "5%" }} > 
        <View style={{ flexDirection: "row" }}> 
          <View style={{ flex: 1, flexDirection: "row", justifyContent: "center", alignContent: "center", alignItems: "center", textAlign: "justify" }} > 
            <Card 
              style={{ padding: 10, elevation: 2, margin: 10, textAlign: "center", width: "95%", borderRadius: 20 }} 
              onPress={onPress} // 调用父组件的点击回调
            > 
              <Card.Content> 
                <Image 
                  source={{ uri: "http://guiadigital.madridactiva.anovagroup.es/" + item.fotos[0] }} 
                  style={{ width: "100%", height: 200, padding: 5, borderRadius: 10 }} 
                  PlaceholderContent={<ActivityIndicator />} 
                /> 
                <Text style={{ color: "#484855", fontSize: RF(3.5), paddingTop: 25, textAlign: "center" }} > 
                  {item.nombre} 
                </Text> 
                <Text style={{ color: "#484855", fontSize: RF(2), paddingTop: 15, textAlign: "center" }} > 
                  {item.descripcion.split(".")[0]} 
                </Text> 
              </Card.Content> 
            </Card> 
          </View> 
        </View> 
      </View> 
    );
  }
}

解决方案二:在Entry组件内独立管理RBSheet(不推荐)

如果你坚持要在每个Entry里保留RBSheet,需要确保:

  1. 使用唯一的keyExtractor避免组件复用
  2. 在Entry组件内用状态控制模态框的显示:
// Entry.js
class Entry extends React.Component {
  state = {
    isModalOpen: false
  };

  openModal = () => {
    this.setState({ isModalOpen: true }, () => {
      this.rbsheetRef.open();
    });
  };

  render() {
    const { item } = this.props;
    return ( 
      <View style={{ flexDirection: "row", alignContent: "center", alignItems: "center", textAlign: "center", marginBottom: "5%" }} > 
        <View style={{ flexDirection: "row" }}> 
          <View style={{ flex: 1, flexDirection: "row", justifyContent: "center", alignContent: "center", alignItems: "center", textAlign: "justify" }} > 
            <Card 
              style={{ padding: 10, elevation: 2, margin: 10, textAlign: "center", width: "95%", borderRadius: 20 }} 
              onPress={this.openModal} 
            > 
              <Card.Content> 
                <Image 
                  source={{ uri: "http://guiadigital.madridactiva.anovagroup.es/" + item.fotos[0] }} 
                  style={{ width: "100%", height: 200, padding: 5, borderRadius: 10 }} 
                  PlaceholderContent={<ActivityIndicator />} 
                /> 
                <Text style={{ color: "#484855", fontSize: RF(3.5), paddingTop: 25, textAlign: "center" }} > 
                  {item.nombre} 
                </Text> 
                <Text style={{ color: "#484855", fontSize: RF(2), paddingTop: 15, textAlign: "center" }} > 
                  {item.descripcion.split(".")[0]} 
                </Text> 
              </Card.Content> 
            </Card> 
          </View> 
        </View> 
        <RBSheet 
          ref={ref => this.rbsheetRef = ref}
          animationType="slide" 
          closeOnPressMask={true} 
          height={RF(80)} 
          duration={100} 
          customStyles={{ 
            container: { 
              justifyContent: "center", 
              alignItems: "center", 
              textAlign: "center", 
              alignContent: "center", 
              borderTopLeftRadius: 20, 
              borderTopRightRadius: 20 
            } 
          }} 
        >
          <Text style={{ color: "#484855", fontSize: RF(3.5), paddingTop: 25, textAlign: "center" }} > 
            {item.nombre} 
          </Text>
        </RBSheet> 
      </View> 
    );
  }
}

同时记得修改FlatList的keyExtractor为唯一值,比如item.id。

关键注意点

  • 始终使用唯一的keyExtractor:这是FlatList避免组件复用异常的核心,尽量用item的唯一ID而不是名称。
  • 复用模态框更高效:多个模态框会增加内存占用,统一在父组件管理是更优的方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 09:15:56