How do you create a basic loading state using html, javascript and reactjs hooks?
Requirements:
1) React functional component.
2) It should just return loading text: "Loading".
3) Show the dots being added incrementally (+1) to the end of the loading text every second.
For example:
Loading.
-1s- Loading..
-1s- Loading...
-1s- Loading
Approach:
Decide the static elements. Then add the dynamics (states, hooks etc). As per the thinking in React doc.
Static element implementation:
1) Create a functional component that returns "Loading".
const Loading = () => {
const loadingText = "Loading";
return (
<div>
<h2>{loadingText}</h2>
</div>
);
};
export default Loading;
Dynamics:
1) The number of dots represents a state of the component. So, define a state variable using useState
. Initial value of the state = 1.
const [dots, setDots] = useState(1);
And add the dots after loading text
{".".repeat(dots)}
2) A state changes automatically after each second. window.setInterval
can perform this task. Leave the callback function empty for now.
window.setInterval(() => {
// Logic to increment dots
}, 1000);
3) Create a useEffect
hook that only runs once after initial render. Notice the empty array as a second argument. Check this Reactjs doc to how second arguments behave differently.
useEffect(() => {
window.setInterval(() => {
// Logic to increment dots
}, 1000);
}, []);
Till now, the app only shows "Loading.".
Take a pause and think of the logic inside window.setInterval
callback function.
The obvious looking solution:
setDots((dots + 1) % 4);
However, the component will only go from
"Loading."-1s-"Loading..". Then it will get stuck.
Reason: The useEffect's
callback fn is triggered on the initial state of the dots (1) only. Any further state changes in dots state does not affect the closure of useEffect's
callback fn.
To achieve proper functionality, update it using previous state as per mentioned in the recatjs docs.
setDots((dots) => (dots + 1) % 4);
The Loading component mounts and unmounts at times. It is a good practice to cleanup every entities that are created during the useEffect's callback and that is not going to get used anymore. In this case, it is the window.setInterval
definition.
The cleanup function runs not only during unmount, but before every re-render with changed dependencies.
The cleanup logic for useEffect hook can be defined using a callback function that gets returned by the callback function of useEffect. Check the example below:
useEffect(() => {
const interval = window.setInterval(() => {
setDots((dots) => (dots + 1) % 4);
}, 1000);
return () => {
window.clearInterval(interval);
};
}, []);
Finally, the implementation of Loading component looks like below:
import { useEffect, useState } from "react";
const Loading = () => {
const [dots, setDots] = useState(0); // defined dots state
const loadingText = "Loading";
// useEffect hook that gets called on initial render of the Loading component
useEffect(() => {
// Defining interval
const interval = window.setInterval(() => {
// Setting state using a previous state
setDots((dots) => (dots + 1) % 4);
}, 1000);
// Cleanup logic:
return () => {
window.clearInterval(interval);
};
}, []);
return (
<div>
<h2>
{loadingText}
{".".repeat(dots)}
</h2>
</div>
);
};
export default Loading;
Top comments (0)