Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for named imports in React.lazy #14603

Closed
gnestor opened this issue Jan 16, 2019 · 12 comments
Closed

Allow for named imports in React.lazy #14603

gnestor opened this issue Jan 16, 2019 · 12 comments

Comments

@gnestor
Copy link

gnestor commented Jan 16, 2019

Do you want to request a feature or report a bug?

Feature

What is the current behavior?

Currently, React.lazy only accepts an anonymous function that returns a dynamic import object. readLazyComponentType then pulls the default export off of that import object. This does not allow for importing named imports from a module.

The official workaround, according to the React docs:

If the module you want to import uses named exports, you can create an intermediate module that reexports it as the default.

Another, more concise workaround:

const Contacts = React.lazy(() =>
  import('./Contacts.js').then(module => ({ default: module.Contacts }))
);

What is the proposed behavior?

I'd be willing to submit a PR that optionally accepts named imports but defaults to importing the default import. It would look like:

thenable.then(
-  moduleObject => {
+  resolvedComponent => {
    if (lazyComponent._status === Pending) {
-      const defaultExport = moduleObject.default;
+      if (resolvedComponent.default) {
+        resolvedComponent = resolvedComponent.default
+      }
      if (__DEV__) {
-        if (defaultExport === undefined) {
+        if (resolvedComponent === undefined) {
          warning(
            false,
-            'lazy: Expected the result of a dynamic import() call. ' +
+            'lazy: Expected a promise that resolves to a React component. ' +
              'Instead received: %s\n\nYour code should look like: \n  ' +
              "const MyComponent = lazy(() => import('./MyComponent'))",
-            moduleObject,
+            resolvedComponent,
          );
        }
      }
      lazyComponent._status = Resolved;
-      lazyComponent._result = defaultExport;
+      lazyComponent._result = resolvedComponent;
    }
  },
  error => {
    if (lazyComponent._status === Pending) {
      lazyComponent._status = Rejected;
      lazyComponent._result = error;
    }
  },
);

This will also require some updates to the docs.

Let me know your thoughts before I start...

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?

react@16.7.0, Chrome 71, macOS 10.14

cc @acdlite @gaearon

@gnestor gnestor changed the title Allow React.lazy to accept non-default imports Allow for named imports in React.lazy Jan 16, 2019
@gaearon
Copy link
Collaborator

gaearon commented Jan 16, 2019

Thanks but it's an intentional design decision to not currently allow named imports. We may reconsider it later. You can find the relevant discussion in reactjs/rfcs#64.

@gaearon gaearon closed this as completed Jan 16, 2019
@nelsieborja
Copy link

This may seem stupid but I expected the feature to have the following syntax:

React.lazy('./MyComponent');

No more extra function or import required

@amitesh786
Copy link

This may seem stupid but I expected the feature to have the following syntax:

React.lazy('./MyComponent');

No more extra function or import required

It's not working, but I have tried this ->

React.lazy(() => import('../MyComponent'), 'default');

It works fine!

@cchamberlain
Copy link

This makes SFC components pretty annoying to use...

@eps1lon
Copy link
Collaborator

eps1lon commented Jun 27, 2019

Summary of why named exports are not baked in and a workaround can be found in reactjs/rfcs#64 (comment)

@dailytabs
Copy link

npm audit said I needed to update to react-scripts 4.0.0 as the only solution to a vulnerability. After upgrade, new ESLint rules went into effect warning about default exports. So, I removed all my default exports and replaced them with named. Now, my router doesn't work.

Thanks so much for not supporting this and making things harder on me. Probably time to reconsider this issue and the comment linked to in the previous comment.

@JoseLion
Copy link

JoseLion commented Nov 13, 2020

Working with default exports on Function Components is very inconvenient. I hope the React team can revisit this soon. IMO React.lazy is a very powerful tool, so I created a small abstraction of @gnestor's workaround to make the lazy import a bit simpler. When used with TypeScript it's 100% type-safe and keeps the look and feel of named imports. I hope this helps! 🙂

function lazyImport<
  T extends React.ComponentType<any>,
  I extends { [K2 in K]: T },
  K extends keyof I,
>(
  factory: () => Promise<I>,
  name: K
): I {
  return Object.create({
    [name]: React.lazy(() => factory().then(module => ({ default:  module[name] })))
  });
}

// Usage
const { Home } = lazyImport(() => import("./Home.component.tsx"), "Home");

PS: The Object.create(..) is to avoid conflicts with the object key and the K type. Another way would be to cast the object as unknown as I, but I think this looks a bit cleaner that way 🙂

@JLarky
Copy link

JLarky commented Dec 1, 2020

@JoseLion you could also use Proxy which allows even crazy stuff like this :)

const { Component1, Component2 } = lazily(() => import('./Components'))

Proxy:

// you can get it from npm `react-lazily`
export function lazily(loader) {
  new Proxy( {}, {
      get: (target, componentName) =>
        React.lazy(() =>
          loader().then((x) => ({ default: x[componentName] }))
        ),
    }
  )
}

You could try it out here https://codesandbox.io/s/react-lazily-example-p7hyj

@dailytabs
Copy link

Seriously? Why not just fix the damn thing! Humans are too content with workarounds. Try to actually fix something and maybe the world won't be such a pile of trash.

@aress31
Copy link

aress31 commented Sep 12, 2021

Any update on that, totally agree with @dailytabs I don't want to rely on a third party library for this.

@mrchantey
Copy link

According to the React code splitting docs:

[limiting to default exports] ensures that tree shaking keeps working and that you don’t pull in unused components.

@can-acar
Copy link

can-acar commented Nov 4, 2021

I get an error when I use React.lazy with sass, for example :
It's working code :
'App.js'

import React, { Suspense, lazy} from "react";
import "./App.scss";


const Test = lazy(() => import("./test"));

const App = () => {
  return (
    <div className={""}>
      <Suspense fallback={<p>....</p>}>
        <Test />
      </Suspense>
    </div>
  );
};

export default App;

BUT!

If I change this code for this example :

import React, { Suspense, lazy, useState, useEffect } from "react";
import "./App.scss";


const load = (path) => {
  let Component= lazy(() => import(`./${path}`));
 return   <Suspense fallback={<p>....</p>}>
                <Component {...this} />
          </Suspense>;
};

const Test = load('test');

const App = () => {
  return (
    <div className={""}>

        <Test />

    </div>
  );
};

export default App;

not working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests