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

React开发中如何阻止图片数组出现不必要的频繁重渲染

问题根因

  1. fetchAssets 函数未做引用缓存,每次组件渲染都会生成新的函数实例,导致依赖它的useEffect反复触发,不断拉取相册资源更新files状态,触发连锁重渲染。
  2. replaceFileState 函数每次渲染都会生成新实例,且依赖外部fileArray状态,状态更新后函数引用也会更新,进一步触发子组件重渲染。
  3. 内联箭头函数回调(如Modal的onBackdropPress)每次渲染生成新引用,若子组件是纯组件也会触发不必要更新。
  4. 自定义组件OnboardingPhoto未做渲染缓存,父组件更新时即使props未变也会跟随重渲染。

修复方案

按以下步骤修改代码即可解决持续重渲染问题:

import {memo, useCallback, useEffect, useState} from 'react';
// 给OnboardingPhoto包memo,props无变化时不会重渲染
const OnboardingPhoto = memo((props) => {
  // 原有OnboardingPhoto逻辑保持不变
})

function OnboardingUploadPhotos() {
  const [modalVisible, setModalVisible] = useState(false);
  const [permissionStatus, setPermissionStatus] = useState('');
  const [files, setFiles] = useState<string[]>([]);
  const [fileArray, setFileArray] = useState<string[]>(['', '', '', '', '', '', '']);

  const setPermissions = useCallback(val => {
    setPermissionStatus(val);
  }, [setPermissionStatus]);

  const setIsModalVisible = useCallback(val => {
    setModalVisible(val);
  }, [setModalVisible]);
  
  // 缓存关闭弹窗的回调,保持引用稳定
  const hideModal = useCallback(() => {
    setIsModalVisible(false);
  }, [setIsModalVisible])

  // 修复1:用useCallback缓存fetchAssets,依赖为空,仅组件挂载时执行一次
  const fetchAssets = useCallback(async () => {
    const recentCameraRoll = await MediaLibrary.getAssetsAsync({first: 11});
    setFiles(recentCameraRoll.assets.slice(1).map(file => file.uri));
  }, []);

  // 修复2:用useCallback缓存replaceFileState,使用函数式更新不依赖外部fileArray
  const replaceFileState = useCallback((file: string, index: number) => {
    setFileArray(prev => {
      const f = [...prev];
      f[index] = file;
      return f;
    });
    // 选完图片需要自动关弹窗可保留这行,不需要就删除
    hideModal();
  }, [hideModal]);

  useEffect(() => {
    fetchAssets();
  }, [fetchAssets]);

  return (
      <View>
        {permissionStatus ?
            <Modal style={styles.bottomModalView} isVisible={modalVisible} backdropOpacity={0}
                   onBackdropPress={hideModal}>
              <View style={styles.modal}>
                <TouchableOpacity>
                  <Text style={{
                    borderBottomWidth: 1,
                    borderBottomColor: '#FFF',
                    color: '#FFF',
                    textDecorationLine: 'underline',
                    alignSelf: 'flex-end',
                    justifyContent: 'center',
                    paddingTop: 40
                  }}>All photos</Text>
                </TouchableOpacity>
                <ScrollView horizontal={true} scrollEnabled={true}
                            contentContainerStyle={{justifyContent: 'center', alignItems: 'center'}}>
                  {files.map((file, index) => {
                    return (
                        // 把key从index换成唯一的file,避免列表渲染异常
                        <TouchableWithoutFeedback key={file} onPress={() => replaceFileState(file, index)}>
                          <Image
                              key={file}
                              style={{width: 100, height: 100, marginLeft: 10, marginRight: 10, borderRadius: 4}}
                              source={{uri: file}}
                          />
                        </TouchableWithoutFeedback>
                    );
                  })}
                </ScrollView>
                <Text style={{fontSize: 22, color: '#FFF', marginLeft: 20, marginBottom: 20}}>Your photos</Text>
              </View>
            </Modal> :
            null
        }
        <Text>Upload your photos</Text>
        <View style={{flexDirection: "row", flexWrap: "wrap", justifyContent: 'space-evenly'}}>
          {fileArray.slice(0,6).map((image, index) => {
            // 移除调试用的console.log,减少性能损耗
            return (
                image != '' ?
                    <Image
                        key={image}
                        style={{width: 100, height: 100, borderRadius: 100}}
                        source={{uri: image}}
                    /> :
                    <OnboardingPhoto key={index} setStatus={setPermissions} setModal={setIsModalVisible}/>
            )
          })}
        </View>
      </View>
  );
}

补充优化说明

如果后续需要实现刷新相册的功能,可以单独给刷新操作绑定fetchAssets调用,不需要修改挂载阶段的useEffect逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 22:39:03