Paul Razvan Berg
Paul Razvan Berg

Reputation: 21400

Can you deconstruct lazily loaded React components?

Using es6 imports, you can do this:

import { MyComponent } from "../path/to/components.js";

export default function () {
  return <MyComponent/>;
}

Can I do it with React.lazy too?

const { MyComponent } = lazy(() => import("../path/to/components.js"));

I get the following error, but I'm not sure if it's related to this or some other bug I have:

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined

Upvotes: 29

Views: 26200

Answers (7)

pope_maverick
pope_maverick

Reputation: 980

Of course you can. It's an honest mistake many has made,

const sub = 'a'
const obj = { a: 'alpha', b: 'beta' }

obj.sub // wrong (accessing a direct key)
obj[sub] // right (computed property)

the same mistake slipped through for many. This is a work in progress but worked like a charm, and thanks for all the other answers to tailor it to my need.

const ComponentFactory = ({ componentName, ...props }) => {
  const Component = lazy(() => import('baseui/typography').then((module) => ({ default: module[componentName] })))

  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Component {...props} />
    </Suspense>
  )
}

usage:

    <ComponentFactory
      componentName='Paragraph1'
      margin='0.1rem 0rem 0.25rem 0.3rem'
      color={style[of].headingText}
    >
      {headingMessage}
    </ComponentFactory>

Upvotes: 6

Lars Ejaas
Lars Ejaas

Reputation: 181

You can resolve a promise along with the lazy loading and this way resolve your named export.

The syntax is a bit funky, but it is working:

const MyComponent = React.lazy(
  () =>
    new Promise(async (resolve) => {
      const module = await import('../path/to/components.js');
      resolve({ ...module, default: module.default });
    }),
);

Upvotes: 0

gxmad
gxmad

Reputation: 2210

Here is how I did it when I faced this problem with FontAwesome:

const FontAwesomeIcon = React.lazy(()=> import('@fortawesome/react-fontawesome').then(module=>({default:module.FontAwesomeIcon})))

Upvotes: 43

JLarky
JLarky

Reputation: 10241

You can if you use react-lazily.

import { lazily } from 'react-lazily';
const { MyComponent } = lazily(() => import("../path/to/components.js"));

It also allows importing more than one component:

const { MyComponent, MyOtherComponent, SomeOtherComponent } = lazily(
  () => import("../path/to/components.js")
);

See this answer for more options.

Upvotes: 22

Ahmed Elazazy
Ahmed Elazazy

Reputation: 484

React.lazy currently only supports default exports. If the module you want to import uses named exports, you can create an intermediate module that reexports it as the default. This ensures that tree shaking keeps working and that you don’t pull in unused components.

// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;
// MyComponent.js
export { MyComponent as default } from "./ManyComponents.js";
// MyApp.js
import React, { lazy } from 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));

More info: https://reactjs.org/docs/code-splitting.html#named-exports

Upvotes: 5

Daniel Einars
Daniel Einars

Reputation: 152

I'd like to another workaround. This compotent chains the promise and adds the named export to the default export. src. Although, I'm not sure if this breaks tree shaking. There's a bit of an explanation here.

import {lazy} from 'react'
export default (resolver, name = 'default') => {
  return lazy(async () => {
    const resolved = await resolver()
    return {default: resolved[name]}
  })
}

Upvotes: 1

giuseppedeponte
giuseppedeponte

Reputation: 2391

You can't with React.lazy :

React.lazy takes a function that must call a dynamic import(). This must return a Promise which resolves to a module with a default export containing a React component. (cf. https://reactjs.org/docs/code-splitting.html#reactlazy)

A workaround for that exists: creating an intermediate module that imports your named export and exports it as default (cf. https://reactjs.org/docs/code-splitting.html#named-exports)

Upvotes: 2

Related Questions