'How to create a progress bar in React?

I would like to add a progress bar to a minicart. The purpose of it is to show to users how much is needed to reach free shipping.

Example: If you have items in your cart for 20$, the bar would be filled by 20%, because free shipping starts from 100$.

This is what I have for now:

const MinimumFreightValue = ({ settings }) => {
    const { binding } = useRuntime();
    const [shippingFreePercentage, setShippingFreePercentage] = useState(0);
    const [differenceBetwenValues, setDifferenceBetwenValues] = useState(0);
    const [freeShippingAmount, setFreeShippingAmount] = useState(0);
    const {
        orderForm: { totalizers },
    } = useOrderForm();

    useEffect(() => {
        if (settings.bindingBounded) {
            const findAmountForBinding = settings.settings?.find((item) => item.bindingId === binding?.id)?.freeShippingAmount;

            if (findAmountForBinding) setFreeShippingAmount(findAmountForBinding);
        } else {
            setFreeShippingAmount(settings.freeShippingAmount);
        }
    }, [binding]);

    const handleUpdateMinicartValue = useCallback(
        (val) => {
            setShippingFreePercentage(Math.round(val / freeShippingAmount));
            setDifferenceBetwenValues(freeShippingAmount - val / 100);
        },
        [freeShippingAmount]
    );

    const getValues = (idValue) => totalizers?.find(({ id }) => id === idValue)?.value ?? 0;

    const finalValue = getValues("Items") + getValues("Discounts");

    useEffect(() => {
        handleUpdateMinicartValue(finalValue);
    }, [handleUpdateMinicartValue, finalValue]);

    return (
        <div className={styles.freigthScaleContainer}>
            {differenceBetwenValues === freeShippingAmount ? (
                <div className={styles.text0}>
                    <FormattedMessage id="store/minicartbar.text0" />
                    <FormattedCurrency value={Math.max(0, differenceBetwenValues)} />!
                </div>
            ) : (
                <>
                    {differenceBetwenValues > 0 ? (
                        <span>
                            <div className={styles.text1}>
                                <FormattedMessage id="store/minicartbar.text1" />
                                <span className={styles.text2}>
                                    <FormattedMessage id="store/minicartbar.text2" />
                                </span>
                            </div>
                        </span>
                    ) : null}
                    <div className={styles.sliderContainer}>
                        <div
                            className={styles.barContainer}
                            style={{
                                width: `${shippingFreePercentage < 100 ? shippingFreePercentage : 100}%`,
                            }}
    

                />
                </div>
                {differenceBetwenValues > 0 ? (
                    <p className={styles.sliderText}>
                        <span className={styles.text3}>
                            <FormattedMessage id="store/minicartbar.text3" />{" "}
                        </span>

                        <span className={styles.currencyText}>
                            <FormattedCurrency value={Math.max(0, differenceBetwenValues)} />!
                        </span>
                    </p>
                ) : (
                    <p className={styles.text4}>
                        <FormattedMessage id="store/minicartbar.text4" />
                    </p>
                )}
            </>
        )}
    </div>
);

};

It's my first time implementing such a thing so I don't know what would be the best approach and what exactly to do, so any advices are welcome!



Solution 1:[1]

You can easily implement a progress bar by using LinearProgress from MUI:

import * as React from 'react'
import Box from '@mui/material/Box'
import LinearProgress from '@mui/material/LinearProgress'

export default function LinearDeterminate()
{
  // progress is a number from 0 to 100
  const [progress, setProgress] = React.useState(0)

  return (
    <Box sx = {{ width: '100%' }}>
      <LinearProgress variant = "determinate" value = {progress} />
    </Box>
  )
}

enter image description here

If you want to implement this from scratch, you can review MUI's source code here to get an idea for how they've done it.

Solution 2:[2]

I would avoid MUI or bootstrap unless you enjoy downloading unnecessary packages/scripts and increasing bundle sizes.

Build your own components and styles IMO, its never usually that much work anyways.

Here is my percentage bar

import { useState, useEffect } from "react";

import Show from "components/ui/Show";
import Button1 from "components/ui/typo/Button1";

import PropTypes from "prop-types";
import Styled from "styled-components";

const Percentage = props => {
    const [width, setWidth] = useState(0);
    useEffect(() => setTimeout(() => setWidth(props.percent), 500), [props.percent]);
    return (
        <Container>
            <Bar>
                <Overlay style = {{ background: props.background, width: `${width}%` }} />
            </Bar>
            <Show show = {Boolean(props.label)}>
                <Button1 text = {`${props.percent}%`} />
            </Show>
        </Container>
    );
};

Percentage.propTypes = {
    percent: PropTypes.number,
    background: PropTypes.string,
    label: PropTypes.bool
};

Percentage.defaultProps = {
    percent: 25,
    background: "whitesmoke",
    label: false
};

const Container = Styled.div`
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
`;

const Bar = Styled.div`
    width: 100%;
    height: 8px;
    margin: 0px 10px 0px 0px;
    background: #efefef;
    border-radius: 12px;
    overflow: hidden;
    position: relative;
`;

const Overlay = Styled.div`
    height: 8px;
    border-radius: 12px;
    position: absolute;
    transition: width 0.25s ease;
`;

export default Percentage;

Simple...

I used styled components so if you do not use that you just need put the css where ever you need it.

Daniel

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 pez
Solution 2 GaddMaster