33

I am trying to use React.lazy for code splitting in my TypeScript React app.

All I am doing is changing that line:

import {ScreensProductList} from "./screens/Products/List";

to this line:

const ScreensProductList = lazy(() => import('./screens/Products/List'));

But the import('./screens/Products/List') part triggers a TypeScript error, stating:

Type error: Type 'Promise<typeof import("/Users/johannesklauss/Documents/Development/ay-coding-challenge/src/screens/Products/List")>' is not assignable to type 'Promise<{ default: ComponentType<any>; }>'.
  Property 'default' is missing in type 'typeof import("/Users/johannesklauss/Documents/Development/ay-coding-challenge/src/screens/Products/List")' but required in type '{ default: ComponentType<any>; }'.

I am not quite sure what I am supposed to do here to get it to work.

Johannes Klauß
  • 10,676
  • 16
  • 68
  • 122

7 Answers7

68

You should do export default class {...} from the ./screens/Products/list instead of export class ScreensProductList {...}.

Or, alternatively, you can do:

const ScreensProductList = lazy(() =>
  import('./screens/Products/List')
    .then(({ ScreensProductList }) => ({ default: ScreensProductList })),
);
Denis Zhbankov
  • 1,018
  • 1
  • 10
  • 10
  • 1
    That works perfectly. And how would I go about lazy loading multiple none default exports from a module? Do I have to do the second approach for each export? – Johannes Klauß Jan 15 '19 at 11:10
  • 1
    @JohannesKlauß, yes, lazy() will only render a single component, so you have to repeat the wrapping for every one needed – Denis Zhbankov Jan 18 '19 at 16:15
  • That's really ugly. Especially since you can only have one default per module. – James Hancock Feb 27 '19 at 19:07
  • What types should be added to this for TypeScript? – reggaeguitar Feb 28 '20 at 17:21
  • @JamesHancock you can check out my answer, react-lazily solves multiple components per module `const { One, Two, Three } = lazily(() => import('./module'))` – JLarky Jan 03 '21 at 05:32
  • @reggaeguitar this already has proper typescript types, you can see typescript example in https://codesandbox.io/s/react-lazily-vs-react-iqy23 – JLarky Jan 03 '21 at 05:57
11

One option is to add default export in "./screens/Products/List" like that

export default ScreensProductList;

Second is to change import code to

const ScreensProductList = React.lazy(() =>
  import("./screens/Products/List").then((module) => ({
    default: module.ScreensProductList,
  }))
);

Or if you don't mind using an external library you could do:

import { lazily } from 'react-lazily';
const { ScreensProductList } = lazily(() => import('./screens/Products/List'));
JLarky
  • 9,833
  • 5
  • 36
  • 37
5

Another solution would be:

1. Import using lazy

const ScreensProductList = lazy(() => import('./screens/Products/List'));

2. Set the type on the export

React hooks

import { FunctionComponent /*, FC */ } from 'react';

const List = () => (
  return </>;
);

export default List as FunctionComponent; // as FC;

React class components

import { Component, Fragment, ComponentType } from 'react';

class List extends Component {
  render() {
    return <Fragment />;
  }
}

export default List as ComponentType;
tim-montague
  • 16,217
  • 5
  • 62
  • 51
vamshi krishna
  • 2,618
  • 1
  • 11
  • 18
1

This is the proper syntax. It works also in the Webstorm IDE (the other syntaxes shown here are still showing a warning)

const ScreensProductList = React.lazy(() => import("./screens/Products/List").then(({default : ScreensProductList}) => ({default: ScreensProductList})));
Reinier Garcia
  • 1,002
  • 1
  • 11
  • 19
1
const LazyCart = React.lazy(async () => ({ default: (await import('../Components/market/LazyCart')).LazyCart }))
Andrew Li
  • 19
  • 3
0

You can create an index.ts file where you can export all your components like in this eg. :

export {default as YourComponentName} from "./YourComponentName";

After that you can use React.lazy:

React.lazy(() => import("../components/folder-name-where-the-index-file-is-created").then(({YourComponentName}) => ({default: YourComponentName})))
C. Draghici
  • 157
  • 5
0

Just to expand on this answer. This also works for the dynamic imports.

const Navbar = dynamic(() => import('../components/Navbar'), {
  ssr: false,
});

Where Navbar is a default exported component.

const Navbar = () => ()


export default Navbar
Bojan Tomić
  • 1,007
  • 12
  • 19