skip to Main Content

I’m building a loading bar in React, but something is not right. I want it to fill width 100% once the timer is reached 3 seconds, but for some reason it does not work as expected.

const { useState, useEffect } = React;

const Loading = () => {
  const [isLoading, setIsLoading] = useState(true);
  const [progress, setProgress] = useState(0);

  useEffect(() => {
    const duration = 3000;
    const increment = 100 / duration;

    const timer = setInterval(() => {
      setProgress((prevProgress) => {
        const newProgress = prevProgress + increment;
        if (newProgress >= 100) {
          clearInterval(timer);
          setIsLoading(false);
          return 100;
        }
        return newProgress;
      });
    }, 10);

    return () => clearInterval(timer);
  }, []);

  return (
    <div className="canvas">
      {isLoading && (
        <div
          className="loadng_bar"
          style={{ width: `${progress}%` }}
        ></div>
      )}
    </div>
  );
};

// Render it
ReactDOM.createRoot(
  document.getElementById("root")
).render(
  <Loading />
);
.loadng_bar {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 5px;
  background-color: #c4483f;
  transition: width 3.05s linear;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>

It should increase the progress up to full width during 3 seconds. How can I achieve it?

2

Answers


  1. It looks like the issue with your loading bar stems from how you’re calculating the increment value and the timing of the setInterval. The goal is to fill the loading bar over a period of 3 seconds, but the way you’re currently doing it may not align with that expectation.

    Here’s a revised version of your code that should work as intended:

    const { useState, useEffect } = React;
    
    const Loading = () => {
      const [isLoading, setIsLoading] = useState(true);
      const [progress, setProgress] = useState(0);
    
      useEffect(() => {
        const duration = 3000; // Duration in milliseconds
        const increment = 100 / (duration / 10); // Calculate increment based on interval time
    
        const timer = setInterval(() => {
          setProgress((prevProgress) => {
            const newProgress = prevProgress + increment;
            if (newProgress >= 100) {
              clearInterval(timer);
              setIsLoading(false);
              return 100;
            }
            return newProgress;
          });
        }, 10); // Update every 10 milliseconds
    
        return () => clearInterval(timer);
      }, []);
    
      return (
        <div className="canvas">
          {isLoading && (
            <div
              className="loading_bar"
              style={{ width: `${progress}%` }}
            ></div>
          )}
        </div>
      );
    };
    
    // Render it
    ReactDOM.createRoot(
      document.getElementById("root")
    ).render(
      <Loading />
    );
    

    CSS

    Make sure your CSS is correctly defined as well:

    .loading_bar {
      position: absolute;
      top: 0;
      left: 0;
      width: 0;
      height: 5px;
      background-color: #c4483f;
    }
    

    Key Changes Explained

    1. Increment Calculation: The increment is now calculated based on how many times the interval runs in the total duration. Since you’re updating every 10 milliseconds, you divide the total duration by the interval time to get the correct number of increments.

    2. CSS Class Name: Ensure that your CSS class name matches what you use in your React component (loading_bar instead of loadng_bar).

    With these adjustments, your loading bar should now fill to 100% over a period of exactly 3 seconds.

    Login or Signup to reply.
  2. Option 1: Animate with CSS only
    You can remove the setInterval and handle the animation with pure CSS by setting the width to 100% initially and letting CSS handle the timing:

    const { useState, useEffect } = React;
    
    const Loading = () => {
      const [isLoading, setIsLoading] = useState(true);
    
      useEffect(() => {
        const timer = setTimeout(() => {
          setIsLoading(false);
        }, 3000); // 3 seconds timer
    
        return () => clearTimeout(timer);
      }, []);
    
      return (
        <div className="canvas">
          {isLoading && (
            <div className="loading_bar"></div>
          )}
        </div>
      );
    };
    
    // Render it
    ReactDOM.createRoot(document.getElementById("root")).render(<Loading />);
    .loading_bar {
      position: absolute;
      top: 0;
      left: 0;
      width: 0;
      height: 5px;
      background-color: #c4483f;
      animation: load 3s linear forwards;
    }
    
    @keyframes load {
      to {
        width: 100%;
      }
    }
    <div id="root"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>

    Option 2: Animate with JavaScript
    If you prefer to control the animation manually with JavaScript using setInterval, you don’t need the CSS transition property. Here’s the corrected approach:

    const { useState, useEffect } = React;
    
    const Loading = () => {
      const [isLoading, setIsLoading] = useState(true);
    
      useEffect(() => {
        const duration = 3000; // 3 seconds
        const stepTime = 10; // Interval time in ms
        const increment = 100 / (duration / stepTime); // Progress increment per interval
    
        const timer = setInterval(() => {
          setProgress((prevProgress) => {
            const newProgress = prevProgress + increment;
            if (newProgress >= 100) {
              clearInterval(timer);
              setIsLoading(false);
              return 100;
            }
            return newProgress;
          });
        }, stepTime);
    
        return () => clearInterval(timer);
      }, []);
    
    
      return (
        <div className="canvas">
          {isLoading && (
            <div
              className="loading_bar"
              style={{ width: `${progress}%` }}
            ></div>
          )}
        </div>
      );
    };
    .loading_bar {
      position: absolute;
      top: 0;
      left: 0;
      height: 5px;
      background-color: #c4483f;
    }
    <div id="root"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search