1

I am having a progress bar which on click starts its progress from 60 to all the way upto 10 for each of the file of files array. I am a using the ref to dymanically increment the progress by 10 and when its 100, I clear it and bring the message as Upload Complete. Code works just fine. I have moved the mechanism of initiating progress onto a custom hook, that basicallly takes setter, the interval and a file name for which it has to update upload progress. I am initializing the hook inside the parent, but each of the file name I will only get when I click each of the file names button. how can I pass the name of the file?

Sandbox: https://codesandbox.io/s/progress-bar-r0zcjn?file=/src/App.js

Component:

import React, { useState, useRef } from "react";
import "./styles.css";
import ProgressBar from "./ProgressBar";
import useProgress from "./useProgress";

const appStyles = {
  height: 20,
  width: 500,
  margin: 50
};

export default function App() {
  const [files, setFiles] = useState([
    { name: "file1", status: 0 },
    { name: "file2", status: 0 },
    { name: "file3", status: 0 }
  ]);

  const initiateProgress = useProgress(setFiles, 60);

  const initiate = (name) => {
    console.log(name);
  };

  return (
    <div className="App" style={appStyles}>
      {files.map((file) => {
        return (
          <div key={file.name}>
            <button type="button" onClick={() => initiate(file.name)}>
              {file.name}
            </button>
            <ProgressBar bgColor={"#DF8100"} progress={file.status} />
            {file.status === 100 && <span>Upload complete</span>}
          </div>
        );
      })}
    </div>
  );
}

Hook

import { useRef } from "react";

const useProgress = (updater, timer, name) => {
  const mockRef = useRef(timer);

  const handleProgress = () => {
    const intervalID = setInterval(() => {
      if (mockRef.current >= 100) {
        clearInterval(intervalID);
        updater((prevState) =>
          prevState.map((item, itemIndex) =>
            item.name === name ? { ...item, status: 100 } : item
          )
        );
      } else {
        mockRef.current = mockRef.current + 10;
        updater((prevState) =>
          prevState.map((item, itemIndex) =>
            item.name === name ? { ...item, status: mockRef.current } : item
          )
        );
      }
    }, 200);
  };

  return handleProgress;
};

export default useProgress;
lrr59
  • 137
  • 5

1 Answers1

1

You need to define another component combining the useProgress hook logic and ProgressBar component. Try like this:

const FileProgress = ({ file, updater }) => {
  const initiateProgress = useProgress(updater, 60, file.name);

  return (
    <div key={file.name}>
      <button type="button" onClick={initiateProgress}>
        {file.name}
      </button>
      <ProgressBar bgColor={"#DF8100"} progress={file.status} />
      {file.status === 100 && <span>Upload complete</span>}
    </div>
  );
};

export default function App() {
  const [files, setFiles] = useState([
    { name: "file1", status: 0 },
    { name: "file2", status: 0 },
    { name: "file3", status: 0 }
  ]);

  return (
    <div className="App" style={appStyles}>
      {files.map((file) => {
        return <FileProgress file={file} updater={setFiles} />;
      })}
    </div>
  );
}

Code sandbox demo:

Edit progress bar (forked)

Amila Senadheera
  • 12,229
  • 15
  • 27
  • 43
  • Actually I cant separate it out as individual components. I want to do call this hook with custom parameters with individual file names. Something like this, https://codesandbox.io/s/progress-bar-forked-lyv7yu?file=/src/App.js – lrr59 Apr 30 '23 at 04:55
  • The above sandbox doesnt work properly. Any alternative than separating it out – lrr59 Apr 30 '23 at 04:56
  • you cannot use the same `useProgress` hook to handle all files as it only instantiates a single counter. That's why I made it a composed component. If you want to you can keep multiple counter states in a single hook but it's better to keep it simple – Amila Senadheera Apr 30 '23 at 07:26