Preloading React components
Tags: react, typescript, javascript, webdev
Published: 2020-04-19 11:33:11

---
title: Preloading React components
published: true
description: How to preload components to improve user experience?
series: React Suspense for Code-Splitting
tags: #react #typescript #javascript #webdev
---
<a href="https://dev.to/marinovicmarko/how-to-use-react-suspense-for-code-splitting-47a5-temp-slug-3733946">In previous post</a> I wrote about code-splitting and how it improves application performance. 
This works great, but what about user experience?  A loader is displayed each time the app needs to load additional code to run. This can get annoying, especially on slower connections. What we can do to improve this is to assume the user's next action. Is user scrolling thought the blog list and hovering over a specific post? If yes, then a user is likely to click on that post to get more info. 
Making this assumption allows us to preload content for the post, rendering preloaded content on the actual click.

## Preloading implementation
I created a simple function `lazyWithPreload` to help in this case. It's a wrapper around `React.lazy` with additional preloading logic that returns special `PreloadableComponent`. 

Code for `lazyWithPreload` and `PreloadableComponent` is available down here:
```js
import { ComponentType } from 'react';

export type PreloadableComponent<T extends ComponentType<any>> = T & {
  preload: () => Promise<void>;
};
```

```jsx
import { lazy, ComponentType, createElement } from 'react';
import { PreloadableComponent } from 'shared/types/component';

const lazyWithPreload = <T extends ComponentType<any>>(
  factory: () => Promise<{ default: T }>
) => {
  let LoadedComponent: T | undefined;
  let factoryPromise: Promise<void> | undefined;

  const LazyComponent = lazy(factory);

  const loadComponent = () =>
    factory().then(module => {
      LoadedComponent = module.default;
    });

  const Component = (props =>
    createElement(
      LoadedComponent || LazyComponent,
      props
    )) as PreloadableComponent<T>;

  Component.preload = () => factoryPromise || loadComponent();

  return Component;
};

export default lazyWithPreload;
```

`lazyWithPreload` take a single argument, `factory` and returns a special component that acts in two different ways. When `preload` is initiated, `factory` gets called, loading the component.
Loaded component is stored and rendered when the app renders `PreloadableComponent`. Another case is when component is not preloaded via `preload`, then `PreloadableComponent` acts like a regular `React.lazy` component.

## Using it with blog list
The idea is to preload content for a post on post title hover. `IBlogPost` has a property `PreloadableContent` which utilizes `lazyWithPreload`. 
```js
import { IBlogPost } from 'shared/types/models/blog';
import lazyWithPreload from 'shared/components/lazy-with-preload';

const post: IBlogPost = {
  id: 2,
  title: 'Whole year of reading (2019)',
  description: 'Complete list of my 2019 reads.',
  date: '2020-01-10',
  slug: 'whole-year-of-reading-2019',
  PreloadableContent: lazyWithPreload(() => import('./content.mdx')),
};

export default post;
```

`BlogListItem` displays preview for single post in the list. Hovering on post title link initializes the content preload. Now the content is loaded and loader will not appear 
when navigating to the post details.
```js
import React from 'react';
import { Link } from '@reach/router';
import { IBlogPost } from 'shared/types/models/blog';
import { StyledContent } from './BlogListItemStyles';

interface IProps {
  post: IBlogPost;
}

const BlogListItem = ({ post }: IProps) => {
  const { title, description, date, slug, PreloadableContent } = post;
  const preloadPost = () => PreloadableContent.preload();

  return (
    <StyledContent>
      <Link to={`/${slug}`} onMouseEnter={preloadPost}>
        {title}
      </Link>
      <span>{date}</span>
      <p>{description}</p>
    </StyledContent>
  );
};

export default BlogListItem;
```

Happy coding 🙌