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

ReactJS:避免将ref传入父组件的实现方案

解决方案:移除onFileUpload回调中的ref参数

以下两种方案都可以实现你的需求,无需将子组件的ref作为回调参数传入父组件:


方案一:父组件集中维护文件-定时器映射表

通过文件名作为唯一标识,在父组件中管理每个文件对应的定时器,不需要子组件传递ref。

1. 修改父组件(App)代码

import { useRef, useState, useEffect } from 'react';
// ... 其他导入和类型定义

const App = () => {
  const [selectedFiles, setSelectedFiles] = useState<FileType[]>([]);
  // 用对象存储每个文件对应的定时器,key为文件名
  const timersRef = useRef<Record<string, ReturnType<typeof setInterval>>>({});

  const onFileUpload = (selectedFile: FileType) => {
    const fileName = selectedFile.file.name;
    // 避免重复创建定时器
    if (timersRef.current[fileName]) return;

    const timer = setInterval(() => {
      setSelectedFiles((prevState) => {
        let shouldClearTimer = false;
        const updatedFiles = prevState.map((currentFile) => {
          if (currentFile.file.name !== fileName) return currentFile;

          // 模拟上传失败逻辑(针对1.pdf)
          if (fileName === "1.pdf" && currentFile.uploadStatus > 40) {
            shouldClearTimer = true;
            return {
              ...currentFile,
              uploadStatus: 0,
              isActive: true,
              uploadError: "Upload Error"
            };
          }

          // 更新上传进度
          const diff = Math.random() * 15;
          const newProgress = Math.min(currentFile.uploadStatus + diff, 100);
          if (newProgress === 100) {
            shouldClearTimer = true;
          }
          return {
            ...currentFile,
            uploadStatus: newProgress
          };
        });

        // 需要停止定时器时,清理资源
        if (shouldClearTimer) {
          clearInterval(timer);
          delete timersRef.current[fileName];
        }
        return updatedFiles;
      });
    }, 60 * Math.floor(Math.random() * 6) + 100);

    // 保存当前文件的定时器
    timersRef.current[fileName] = timer;
  };

  // 组件卸载时清除所有定时器,避免内存泄漏
  useEffect(() => {
    return () => {
      Object.values(timersRef.current).forEach(timer => clearInterval(timer));
      timersRef.current = {};
    };
  }, []);

  // ... 文件选择、子组件渲染等其他逻辑
};

2. 修改子组件代码

移除回调中的ref参数,只传递file对象:

import { useRef, useEffect } from 'react';
// ... 其他导入和类型定义

const FileUploader = ({ file, onFileUpload }: { file: FileType; onFileUpload: (file: FileType) => void }) => {
  useEffect(() => {
    if (file.active && !file.uploadError) {
      onFileUpload(file);
    }
  }, [file.active, file, onFileUpload]);

  // ... 组件渲染逻辑
};

方案二:子组件自治管理定时器,仅向父组件汇报状态

将定时器的启停逻辑移到子组件内部,子组件只负责计算进度/错误,通过回调通知父组件更新状态,完全解耦定时器管理和全局状态维护。

1. 修改子组件代码

import { useRef, useEffect } from 'react';

interface FileUploaderProps {
  file: FileType;
  onUpdateFile: (updatedFields: Partial<FileType>) => void;
}

const FileUploader: React.FC<FileUploaderProps> = ({ file, onUpdateFile }) => {
  const timerRef = useRef<ReturnType<typeof setInterval>>();

  useEffect(() => {
    if (file.active && !file.uploadError) {
      timerRef.current = setInterval(() => {
        let updatedStatus: Partial<FileType> = {};
        let shouldStopTimer = false;

        // 模拟上传失败
        if (file.file.name === "1.pdf" && file.uploadStatus > 40) {
          updatedStatus = {
            uploadStatus: 0,
            uploadError: "Upload Error"
          };
          shouldStopTimer = true;
        } else {
          // 更新进度
          const diff = Math.random() * 15;
          const newProgress = Math.min(file.uploadStatus + diff, 100);
          updatedStatus = { uploadStatus: newProgress };
          if (newProgress === 100) {
            shouldStopTimer = true;
          }
        }

        // 通知父组件更新状态
        onUpdateFile(updatedStatus);

        // 停止定时器
        if (shouldStopTimer) {
          clearInterval(timerRef.current);
        }
      }, 60 * Math.floor(Math.random() * 6) + 100);
    }

    // 清理逻辑:组件卸载或文件状态变化时清除定时器
    return () => {
      if (timerRef.current) {
        clearInterval(timerRef.current);
      }
    };
  }, [file, onUpdateFile]);

  // ... 组件渲染逻辑
};

2. 修改父组件(App)代码

实现状态更新回调:

const App = () => {
  const [selectedFiles, setSelectedFiles] = useState<FileType[]>([]);

  const onUpdateFile = (fileName: string, updatedFields: Partial<FileType>) => {
    setSelectedFiles(prevState => 
      prevState.map(file => 
        file.file.name === fileName ? { ...file, ...updatedFields } : file
      )
    );
  };

  // 渲染子组件时传递回调
  return (
    <div>
      {/* ... 文件选择逻辑 */}
      {selectedFiles.map(file => (
        <FileUploader 
          key={file.file.name} 
          file={file} 
          onUpdateFile={(updated) => onUpdateFile(file.file.name, updated)} 
        />
      ))}
    </div>
  );
};

方案对比

  • 方案一:父组件集中控制所有上传任务,适合需要全局暂停/取消所有上传的场景,逻辑集中但耦合度稍高。
  • 方案二:子组件职责更清晰,符合React组件设计原则,父组件只负责状态维护,耦合度更低。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 12:55:25