Development build crashes when using React.lazy() on a file that does not exist
Created by: k1sul1
Describe the bug
You might think "no shit, Sherlock." after reading the title. Let me preface this a bit. I'm creating a single page application that talks to WordPress REST API. I've made some changes to it, so I can get Gutenberg block data as JSON, which is about 100 times nicer to deal with than HTML. WordPress core blocks in the data are still mostly HTML, and most components can be rendered with the same component, but I don't want to maintain a map of which block uses which component, because that map would be out of date when a new block is added to the WordPress core.
So I've done this.
// I've a renderBlocks function that parses the block JSON and uses the block name to determine
// which component to load. This is a pseudocode version.
function renderBlocks(blocks) {
return (
<Suspense fallback={<p>Loading</p>}>
{blocks.map(block => {
let Component
Component = React.lazy(() => import(`../components/blocks/core/index`))
return <Component {...props} key={id} />
})}
</Suspense>
)
}
// in components/blocks/core/index
export default class CoreBlock extends Component {
render() {
const { componentName, ...props } = this.props
const Component = React.lazy(() => {
return import(`./${componentName}`)
.catch((e) => {
console.log('WTF?')
console.error(e)
return {
default: () => <DefaultBlock {...props} />,
}
})
})
return (
<ErrorBoundary>
<Suspense fallback={<div>Loading WordPress block...</div>}>
<Component {...props} />
</Suspense>
</ErrorBoundary>
)
}
}
class DefaultBlock extends Component {
render() {
return (
<HTML {...this.props} />
)
}
}
If there's a List block in the content, this code will try to import a List component, but if it doesn't exist, it will return the default component. This allows me to only create components for the blocks I want to handle differently, and all I need to do is create a file in the directory, no need to add any code anywhere else.
It works, in the production build. The development build crashes with error "Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.". AFAIK, this error usually appears when you've code that swallows errors, but in this case, I need to swallow the error, and I don't give a fuck about the file not existing.
I guess that the Error: Cannot find module './List' error that's caused by the failing dynamic import is causing this, but how do I avoid it?
Did you try recovering your dependencies?
Yes. Npm version 6.9.0.
Which terms did you search for in User Guide?
I've looked at the Suspense docs but it's not much.
Environment
Environment Info:
System: OS: Linux 5.1 Arch Linux CPU: (8) x64 Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz Binaries: Node: 10.16.0 - ~/.nvm/versions/node/v10.16.0/bin/node Yarn: Not Found npm: 6.9.0 - ~/.nvm/versions/node/v10.16.0/bin/npm Browsers: Chrome: Not Found Firefox: 67.0.4 npmPackages: react: ^16.8.6 => 16.8.6 react-dom: ^16.8.6 => 16.8.6 react-scripts: 3.0.0 => 3.0.0 npmGlobalPackages: create-react-app: Not Found
Expected behavior
I expected that I could try to import a file and fallback to the default if the file doesn't exist. I can, but only in the production build.
Actual behavior
The app crashes, and the root error boundary shows this error.
I can see this in the console. The components render correctly for a split second before the crash.