My goal is to incrementally increase the progress bar when some time have passed after creating an order. To do this, I'd like to call the updateStatus
async function below at different intervals and with different arguments until the progress
state reaches 100. Below I've pasted my code bit.
Chaining setTimeOut
and Promise
s resulted in the progress bar increasing and decreasing randomly and causing the app to slow down / freeze. What is the proper way to handle what I'm trying to achieve?
const ProgressBar = ({ order }) => {
const { status, order_date } = order;
const msTillXSeconds = (seconds) => new Date(moment(order_date).add(seconds, 'seconds')).getTime() - Date.now();
const [progress, setProgress] = useState(progression[status]);
const [internalStatus, setInternalStatus] = useState(status);
const updateStatus = async (newStatus) => {
try {
const body = { status: newStatus };
const response = await fetch(`http://localhost:5000/orders/${order.order_id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json', token: localStorage.token },
body: JSON.stringify(body),
});
if (response.ok) {
setProgress(progression[newStatus]);
setInternalStatus(newStatus);
}
} catch (err) {
console.error(err.message);
}
};
useEffect(() => {
setProgress(progression[status]);
}, []);
return (
<>
<div className="progress">
<div
className="progress-bar progress-bar-striped progress-bar-animated"
role="progressbar"
style={{ width: `${progress}%` }}
>
{' '}
</div>
</div>
<p className="text-center my-1">{internalStatus}</p>
</>
);
};
const STAGES = {
PENDING: 'Pending Order Confirmation',
CONFIRMED: 'Order Confirmed',
PICKING_UP: 'Picking Up',
EXAMINING: 'Examining',
SANITIZING: 'Sanitizing',
RECYCLING: 'Recycling',
};
const progression = {
[STAGES.PENDING]: 5,
[STAGES.CONFIRMED]: 20,
[STAGES.PICKING_UP]: 40,
[STAGES.EXAMINING]: 60,
[STAGES.SANITIZING]: 70,
[STAGES.RECYCLING]: 100,
};
CodePudding user response:
You can pass different arguments to your function as a third argument of setTimeout.
setTimeout(updateStatus, 1000, ...args)
(using your internalStatus
as status
in my example)
Also, since status is in your state, you can handle a next setTimeout on a useEffect() hook depending on your logic.
useEffect(() => {
if status something
otherStatus = somethingElse
updateOtherStatusAfter = ...
setTimeout(updateStatus, updateOtherStatusAfter, otherStatus)
}, [ status ] )
Observe that status is in the dependency list of useEffect.
This means that every time the status changes, the function that you pass inside the useEffect will be triggered.
After a comment of Drew Reese about clean up, consider something like this.
useEffect(() => {
const tId = setTimeout(...)
return () => {
clearTimeout(tId)
}
}, [ ... ] )
CodePudding user response:
If I understand correctly your problem, you want to call periodically updateStatus AND ensure that the order of updateStatus call is respected.
For this I see two solutions :
- call updateStatus recursively (ie at the end of the function add a setTimeout(...))
- use a promise queue (like https://www.npmjs.com/package/queue-promise) and push to it using a setInterval
In any case, you will need to clean these when component unmounts