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

FlashList滚动时出现图片加载错乱问题求助

FlashList滚动时图片错乱问题解决

问题描述

使用FlashList展示包含头像、标题的列表时,滚动出现图片错乱(加载错误图片)的情况。原因是FlashList的Recycling机制:组件移出视口时不会被销毁,而是被复用并传入新的item属性。当前renderItem返回的组件层级使用了useState,且尝试通过对比id重置状态的方法无效。

相关版本:

  • react-native: 0.70
  • @shopify/flash-list: 1.4.3

问题代码

renderItem代码

const renderItem = useCallback(
    ({ item }: ListRenderItemInfo<Avatar>) => (
        <ListItemAvatar
          logoUri: getLogos(item),
          id: item.name + index
        />
    ),
    []
);

Avatar组件代码

type Avatar = {
  id?: string;
  logoUri?: ImageURISource | Array<ImageURISource>;
};

export const Avatar = ({ id, logoUri }: AvatarProps) => {
  const indexValue = useRef<number>(0);
  const lastItemId = useRef(id);

  const [sourceImage, setSourceImage] = useState(
    logoUri === undefined
      ? undefined
      : Array.isArray(logoUri)
      ? logoUri[0]
      : logoUri
  );

  if (id !== lastItemId.current) {
    console.log(`${lastItemId.current} has been recycled to ${id}`);

    lastItemId.current = id;
    setSourceImage(
      logoUri === undefined
        ? undefined
        : Array.isArray(logoUri)
        ? logoUri[indexValue.current]
        : logoUri
    );
  }

  const onError = () => {
    if (Array.isArray(logoUri) && indexValue.current + 1 < logoUri.length) {
      indexValue.current = indexValue.current + 1;
      setImageSource(logoUri[indexValue.current]);
      return;
    }
    setSourceImage(undefined);
  };

  return (
    <View>
      {sourceImage === undefined ? (
        <Icon />
      ) : (
        <Image source={sourceImage} onError={onError}
        />
      )}
    </View>
  );
};

解决方案

1. 用useEffect正确重置状态(修复原代码逻辑)

原代码在渲染阶段直接修改状态,违反React渲染规则,导致状态重置无效。改用useEffect监听id变化,在副作用中统一重置所有状态:

export const Avatar = ({ id, logoUri }: AvatarProps) => {
  const indexValue = useRef<number>(0);
  const [sourceImage, setSourceImage] = useState<ImageURISource | undefined>(
    logoUri === undefined
      ? undefined
      : Array.isArray(logoUri)
      ? logoUri[0]
      : logoUri
  );

  // 监听id变化,重置重试索引和图片源
  useEffect(() => {
    indexValue.current = 0;
    const initialSource = logoUri === undefined
      ? undefined
      : Array.isArray(logoUri)
      ? logoUri[0]
      : logoUri;
    setSourceImage(initialSource);
  }, [id, logoUri]);

  const onError = () => {
    if (Array.isArray(logoUri) && indexValue.current + 1 < logoUri.length) {
      indexValue.current += 1;
      setSourceImage(logoUri[indexValue.current]);
      return;
    }
    setSourceImage(undefined);
  };

  return (
    <View>
      {sourceImage === undefined ? (
        <Icon />
      ) : (
        <Image source={sourceImage} onError={onError} />
      )}
    </View>
  );
};

2. 移除本地状态,基于props推导(推荐)

遵循官方建议,尽量不在renderItem返回的组件中使用本地状态,改用useMemo结合useState(仅保留重试索引),完全基于props计算当前图片源:

export const Avatar = ({ id, logoUri }: AvatarProps) => {
  const [retryIndex, setRetryIndex] = useState(0);

  // 基于logoUri和retryIndex计算当前要显示的图片源
  const currentSource = useMemo(() => {
    if (!logoUri) return undefined;
    if (Array.isArray(logoUri)) {
      // 确保索引不越界
      return logoUri[Math.min(retryIndex, logoUri.length - 1)];
    }
    return logoUri;
  }, [logoUri, retryIndex]);

  // 组件复用(id变化)时重置重试索引
  useEffect(() => {
    setRetryIndex(0);
  }, [id]);

  const onError = () => {
    if (Array.isArray(logoUri) && retryIndex + 1 < logoUri.length) {
      setRetryIndex(prev => prev + 1);
    }
  };

  return (
    <View>
      {currentSource === undefined ? (
        <Icon />
      ) : (
        <Image source={currentSource} onError={onError} />
      )}
    </View>
  );
};

3. 强制组件重新挂载(性能妥协方案)

给Avatar组件添加唯一key,让React在item变化时销毁旧组件、创建新组件,彻底避免复用带来的状态问题。但会损失FlashList的复用性能,仅在其他方案无效时使用:

const renderItem = useCallback(
  ({ item, index }: ListRenderItemInfo<Avatar>) => {
    const itemId = `${item.name}-${index}`;
    return (
      <ListItemAvatar
        key={itemId}
        logoUri={getLogos(item)}
        id={itemId}
      />
    );
  },
  []
);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 14:55:57