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

React Native FlatList点击删除按钮重渲染全列表导致头像背景色异常变更问题求助

问题根因及修复方案

核心问题点

    1. FlatList的keyExtractor使用了随机值:每次组件重渲染时所有列表项的key都会变化,React会直接销毁整个列表重新渲染,所有头像都会重新生成随机色
    1. 子组件定义在父组件内部:InitialIcon、recentContactCards都定义在RecentSaved组件内部,只要父组件state(弹窗显隐状态visible)变化,这些子组件会被重新创建,触发全量重绘
    1. 随机色无缓存:每次渲染头像都会执行generateColor生成新颜色,没有和联系人ID绑定

具体修复步骤

步骤1:修复keyExtractor

将原来的随机key替换为联系人唯一ID,保证同一列表项key稳定:

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

步骤2:抽离子组件+随机色缓存

将子组件移出RecentSaved避免重复创建,同时新增颜色缓存逻辑,同一联系人ID只会生成一次颜色:

// 放到RecentSaved组件外部,全局样式也放在文件顶部保证所有组件可访问
const generateColor = () => {
  const randomColor = Math.floor(Math.random() * 16777215)
    .toString(16)
    .padStart(6, '0');
  return `#${randomColor}`;
};

const InitialIcon = ({contactId, initials, colorMap}) => {
  // 有缓存直接用,没有就生成后存入缓存
  const bgColor = colorMap.current.get(contactId) || (() => {
    const newColor = generateColor();
    colorMap.current.set(contactId, newColor);
    return newColor;
  })();
  return (
    <View
      style={[
        Styles.initialsIconContainer,
        {backgroundColor: bgColor},
      ]}>
      <Text style={Styles.initialsIconText}>{initials}</Text>
    </View>
  );
};

const RecentContactCard = React.memo(({item, onDelete, colorMap}) => {
  const initials = item.name
    ? item.name.slice(0, 2).toUpperCase()
    : `+${item.number.slice(0, 2)}`;
  return (
    <View style={Styles.flatListCard}>
      <View style={Styles.flatListSubView}>
        <View style={{flexDirection: 'row', marginLeft: 10}}>
          <InitialIcon contactId={item.id} initials={initials} colorMap={colorMap} />
          <View style={{justifyContent: 'center'}}>
            {item.name ? (
              <Text style={Styles.nameTitle}>{item.name}</Text>
            ) : null}
            <Text style={item.name ? Styles.numberTitle : Styles.nameTitle}>
              +{item.number}
            </Text>
          </View>
        </View>
        <View style={{flexDirection: 'row'}}>
          <TouchableOpacity style={{marginRight: 15}}>
            <WhatsappIcon width={25} height={25} />
          </TouchableOpacity>
          <TouchableOpacity
            onPress={() => onDelete(item.id)}
            style={{marginRight: 10}}>
            <DeleteIcon width={25} height={25} />
          </TouchableOpacity>
        </View>
      </View>
      <View style={Styles.separatorLine} />
    </View>
  );
});

步骤3:组件内部优化

在RecentSaved内部新增颜色缓存ref、待删除ID状态,同时用useCallback包裹回调函数避免重复生成:

const RecentSaved = ({navigation}) => {
  const [recentSavedContacts, setRecentSavedContacts] = useState([]);
  const [visible, setVisible] = useState(false);
  const [deleteId, setDeleteId] = useState(null);
  // 组件内缓存颜色,避免全局变量污染
  const colorMap = useRef(new Map());

  useEffect(() => {
    const tasks = realm.objects('Contact');
    setRecentSavedContacts([...tasks]);
    tasks.addListener(() => {
      setRecentSavedContacts([...tasks]);
    });

    return () => {
      tasks.removeAllListeners();
      realm.close();
    };
  }, []);

  const onDelete = useCallback((id) => {
    setDeleteId(id);
    setVisible(true);
  }, []);

  const renderItem = useCallback(({item}) => {
    return <RecentContactCard item={item} onDelete={onDelete} colorMap={colorMap} />
  }, [onDelete]);
  // ... 其余代码保持不变

步骤4:修改弹窗确认逻辑

给确认按钮添加实际删除逻辑,删除同时清理对应颜色缓存:

<TouchableOpacity
  style={{
    borderWidth: 1,
    borderRadius: 10,
    padding: 10,
    backgroundColor: 'red',
    width: '40%',
  }}
  onPress={() => {
    setVisible(false);
    // 清理已删除联系人的颜色缓存
    colorMap.current.delete(deleteId);
    deleteContactList(deleteId)
      .catch(error => {
        alert(`Failed to delete contact with id = ${deleteId}, error=${error}`);
      });
  }}>
  <Text style={[Styles.nameTitle, {marginLeft: 15, color: 'white'}]}>
    Confirm
  </Text>
</TouchableOpacity>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 22:57:01