loadable-components: Failing to import named exports server side
š Bug Report
When importing a named export from another file, I receive the following error
{
"status": "error",
"message": "Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.",
"stack": [
"Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.",
"at invariant (webpack:///../node_modules/react-dom/cjs/react-dom-server.node.development.js?:58:15)",
"at ReactDOMServerRenderer.render (webpack:///../node_modules/react-dom/cjs/react-dom-server.node.development.js?:3395:7)",
"at ReactDOMServerRenderer.read (webpack:///../node_modules/react-dom/cjs/react-dom-server.node.development.js?:3131:29)",
"at renderToString (webpack:///../node_modules/react-dom/cjs/react-dom-server.node.development.js?:3598:27)",
"at eval (webpack:///./src/server/render.jsx?:40:90)","at Layer.handle [as handle_request] (webpack:///../node_modules/express/lib/router/layer.js?:95:5)",
"at trim_prefix (webpack:///../node_modules/express/lib/router/index.js?:317:13)","at eval (webpack:///../node_modules/express/lib/router/index.js?:284:7)",
"at Function.process_params (webpack:///../node_modules/express/lib/router/index.js?:335:12)","at next (webpack:///../node_modules/express/lib/router/index.js?:275:10)"
]
}
To Reproduce
Express SSR setup
App.js
import React from 'react'
import loadable from '@loadable/component'
import './main.css'
const A = loadable(async () => {
const { A: AComponent } = await import('./letters/A')
return () => <AComponent />
})
const App = () => (
<div>
<A />
</div>
)
export default App
./letters/A.js
export const A = () => 'A'
Expected behavior
Named export should resolve server side and render correctly
Link to repl or repo (highly encouraged)
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 25 (6 by maintainers)
I would love to see this feature implemented! I want to group several components from one entry point without creating a massive amount of extra chunks.
For example, a profile page which is only loaded after login and that āchunkā contains the profile subpages. The way itās setup now I get multiple chunks for each subroute under the profile namespace.
And still in high demand.
You might find more details on the PRs, but look like we are š¢
Sorry Stalebot, but itās nearly done.
That sounds like a good convention, and sure, if everyone 100% follows it always it works. The way I like to do it is to always use named exports and never use default exports. If you see a named export, you can 100% grep for it and it will work, you can be sure of it. With your convention its still possible that someone directly imported one of the default exports in the internal implementation (maybe they shouldnāt, but they did) - and then if you change it something breaks. Even if thatās rare, if its not 100% you canāt trust it and refactoring is slower.
I take your point - its not necessary for loadable components to support named exports, but I think it is useful and important. I like to use an eslint rule enforcing named exports - so to use loadable-components I have to make the eslint config more complicated to add exceptions for loadable-components, or drop the rule (and then probably have default exports everywhere again).
The ālarge problemā IMO is the surprising different behaviour on SSR vs client if you try to wrap the promise (which can very directly cause bugs). To solve that, the best idea I can think of is to do a separation in the API like
createImportvswatchdog, and then make the babel plugin detect if the return value is not a directimport()statement and error otherwise. I think that would be simple to implement, and it would solve this issue. AddingresolveComponentwould mean not breaking support for named exports for people who donāt use SSR.createImportinstead also works, but the babel plugin can enforce thatcreateImportis only() => import(<path>), not() => someWrapperWhichFailsServerSide(import(<path>))(This seems harder for the babel plugin, but easier for code analysis tools)XYZ, all files that import it contain the stringXYZ. Not true with default exports. Also IDE autocomplete for imports is unlikely to work well with default exports, but it works great for named exports in VSCode. Named exports is the only reason I would want to useresolveComponent, but I think itās a good reason.resolveComponentcould be replaced with akey(the exported name with the component), but I think this would be harder to type.watchdogfrom the import promise because that makes it clear that the user is not allowed change the return value of the import promise, and allows the babel plugin to enforce that without breaking the ability to have timeouts and delays.