I am having a progress bar which on click starts its progress from 60 to all the way upto 100. I am able to achieve this using settinterval and setting the corresponding state.
import React, { useState, useRef } from "react";
import ProgressBar from "./ProgressBar";
export default function App() {
const [file, setFile] = useState({ status: 0, error: "" });
const mockRef = useRef(60);
const initiate = () => {
const intervalID = setInterval(() => {
if (mockRef.current >= 100) {
clearInterval(intervalID);
setFile((prevState) => ({
...prevState,
status: 100
}));
} else {
setFile((prevState) => ({
...prevState,
status: mockRef.current
}));
mockRef.current = mockRef.current + 10;
}
}, 200);
};
return (
<div className="App" style={appStyles}>
<button type="button" onClick={initiate}>
Click Me!
</button>
<ProgressBar bgColor={"#DF8100"} progress={file.status} />
{file.status === 100 && <span>Upload complete</span>}
</div>
);
}
I am 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.
Sandbox: https://codesandbox.io/s/simple-react-progress-bar-forked-yfz9xb?file=/src/useProgress.jsx:0-436
Now I want the content inside of initiate
to move it to a custom hooks that should take care of the setinterval functionality and set the object so that the functionality remains the same. May be this cutsom hook should take initial percentage as number and may be the state setter.
Any idea how I can write this custom hook.This is what my attempt looks like
import { useRef } from "react";
export const useProgress = (state, timer) => {
const mockRef = useRef(timer);
const intervalID = setInterval(() => {
if (mockRef.current >= 100) {
clearInterval(intervalID);
return {
...state,
status: 100
};
} else {
mockRef.current = mockRef.current + 10;
return {
...state,
status: mockRef.current
};
}
}, 200);
};
2
Answers
When you have something working within a function component that you want to turn into a hook, it’s often easier than one might think. Usually, you can copy the functioning code you have directly into the hook; make the hook accept anything it needs from outside that code as parameters; and return whatever the calling component will need from the hook as a return value (if there are multiple values, wrap them in an array [tuple] or object).
In your case, just copy everything prior to the
return
from your component into a hook, and have the hook returnfile
andinitiate
:Then
App
uses it like this:Updated sandbox
You can provide
updater
handler function and thetimer
start value as arguments which can change depending on where you use your custom hook is used.This would also work:
In your
App.jsx
Codesandbox Demo:
If the state is an array then
fileIndex
can be passed into the custom hook: