Avoiding ref to be passed to the parent component: ReactJS

Viewed 143

I am having a file uploader which is a child component. The state of the files are being maintained in tha App component. I am trying to mock, the upload and failure cases inside App component itself using setInterval. But I am using passing a ref from the child component to the parent callback, just so that I get the latest upload status of a file inside setTimeout. Implementation works just fine, Is there someway where I can do all this, without passing down any ref to the parent component inside callback?

I am able to successfully mock the completion and failure. In the below sandbox, I am hardcoding the file name to "1.txt" so that uploads fail just for that.

Note: Its ok if I have to create any ref's inside App, I just dont want the ref to be part of onFileUpload callback when passed to the App component

Sandbox: https://codesandbox.io/s/updated-file-upload-forked-k59xgo

Inside File Upload

 const timerRef = useRef<ReturnType<typeof setInterval> | undefined>();

  useEffect(() => {
    if (file.active && !file.uploadError) {
      onFileUpload(file, timerRef);
    }
  }, [file.active, file, onFileUpload]);

Inside App

 const onFileUpload = (
    selectedFile: FileType,
    uploadRef: React.MutableRefObject<
      ReturnType<typeof setInterval> | undefined
    >
  ) => {
    pollRef.current = selectedFile;
    if (!uploadRef.current) {
      uploadRef.current = setInterval(() => {
        if (
          pollRef.current?.uploadStatus === 100 ||
          pollRef.current?.uploadError
        ) {
          clearInterval(uploadRef.current);
        }
        const diff: number = Math.random() * 15;
        let currentProgess: number = pollRef.current?.uploadStatus as number;
        setSelectedFiles((prevState: FileType[]) => {
          return prevState.map((currentFile: FileType) => {
            const {
              file: { name: currentFileName },
              uploadStatus: currentFileUploadStatus
            } = currentFile;
            if (currentFileName === selectedFile.file.name) {
              if (
                selectedFile.file.name === "1.pdf" &&
                currentFileUploadStatus > 40
              ) {
                clearInterval(uploadRef.current);
                return {
                  ...currentFile,
                  uploadStatus: 0,
                  isActive: true,
                  uploadError: "Upload Error"
                };
              }
              currentProgess = Math.min(currentFileUploadStatus + diff, 100);
              return {
                ...currentFile,
                uploadStatus: currentProgess
              };
            } else return currentFile;
          });
        });
        return currentProgess;
      }, 60 * Math.floor(Math.random() * 6) + 100);
    }
  };
1 Answers

You already use the file name as key for React.

<ListItem key={file.file.name} className={"file-name"}>

You could also use it as a key for the timeouts.

Effectively, that's what already happens. You rely on React rendering your unique component to get the right timeout reference.

This allows you to remove uploadRef, also from the UploadFile component as it was only used for this timeout.

const fileIntervals = useRef({});
const onFileUpload = ( selectedFile: FileType ) => {
  // ...
  const { name } = selectedFile;
  if (fileIntervals.current[name]) {
    return;
  }
  fileIntervals.current[name] = setInterval( // ...
Related