Today we are going to learn why I needed to create such a component and how I went about doing it with setTimeout() and understanding why Reacts render method works in a synchronous way and how the hooks API can be used to create a delayed list effect.
So if you try and do what is in the code example below in React it won't work and render method will wait for setTimeout() to complete by which time all your components will appear in one go and you can't add fancy animation (a list item in this case).
const todos = [
{
name: 'Learn how to create a delayed component',
isComplete: false
},
{
name: 'Like this post',
isComplete: false
},
{
name: 'Share this post (see what I am getting at here) 😀',
isComplete: false
}
];
..and our Todos component (the wrong way):
import React from 'react';
export default TodoList = () => {
return (
<ul>
{todos.map(({ name, isComplete }, index) =>
setTimeout(() => {
<li key={index} className={`${isComplete ? 'isComplete' : ''}`}>
{name}
</li>;
}, index * 200)
)}
</ul>
);
};
Now the <TodoList />
component may or may not do what you think it would. Reacts render process is synchronous meaning when setTimeout()
is called it will wait until the timer function has finished then render the elements so you will get no delay when each list item renders.
Let's try that again, but first, we need to create our <DelayedComponent/>
import React from 'react';
export default DelayedComponent = ({ wait, children }) => {
const [isShown, setIsShown] = React.useState(false);
React.useEffect(() => {
const timer = setTimeout(() => {
setIsShown(true);
}, wait);
return () => clearTimeout(timer);
}, [wait]);
return isShown && children;
};
Let's walk through how this works. We need to declare two props I have named them wait
this accepts the amount of time the component should wait until it should be rendered and the other is a standard prop name which is always called children
and allows you to render any child elements nested within <DelayedComponent/>
i.e:
<DelayedComponent>
{/* start child elements */}
<h1>This is a title</h1>
<div>This is the main content</div>
{/* end child elements */}
</DelayedComponent>
Then we declare the state for showing the intended element/child. For this, we can make use of the useState()
hook. We can set the initial value of this to false
.
Now, things get a little interesting as we start to use useEffect()
hook which can be really powerful when implemented correctly. useEffect()
is essentially the componentDidMount()
lifecycle method we get on class-based components and when you return
inside the useEffect()
hook that is also similar as componentWillUnmount()
we again have on class-based components.
So what we have done is assigned the variable timer
to the setTimeout()
function and passed in the wait
prop to the millisecond's parameter for the timer function.
React.useEffect(() => {
const timer = setTimeout(() => {
setIsShown(true);
}, wait);
return () => clearTimeout(timer);
}, [wait]);
If you look at the final argument of the useEffect()
hook you will see we also pass in an array. This is the dependency array and what this does is keeps track of anything we pass in so in our case it would be the wait
prop. Finally we clean up the timer function by clearing it with clearTimeout()
Top comments (0)