DEV Community

Joe Hill
Joe Hill

Posted on • Edited on

Create a delayed component

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
  }
];
Enter fullscreen mode Exit fullscreen mode

..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>
  );
};
Enter fullscreen mode Exit fullscreen mode

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;
};
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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]);
Enter fullscreen mode Exit fullscreen mode

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)