dynamically import a React Component if that file exists, otherwise show a default message

Viewed 7181

I want to conditionally import a React Component if the file exists and if not do something else. For example show a default view or message.

I tried this:

let Recipe;
try {
  Recipe = require(`docs/app/Recipes/${props.componentName}`);
} catch (e) {
  Recipe = () => <div>Not found</div>;
}

However the linter is complaining that I should not try to dynamicaly require a file, but use a string literal instead.

Is there a cleaner approach to to what I'm trying to achieve?

2 Answers

The problem is this approach is that it kills bundle optimizations and includes all files from docs/app/Recipes/ into a bundle, even if they aren't used.

A better way to write this is to use <React.Suspense> and React.lazy:

const Recipe = React.lazy(() =>
  import(`docs/app/Recipes/${props.componentName}`)
  .catch(() => ({ default: () => <div>Not found</div> }))
);

Which is used as:

<React.Suspense fallback={'loading...'}><Recipe/></React.Suspense>

A cleaner way to do this and avoid linter error is to have a map of possible components:

import Foo from 'docs/app/Recipes/Foo';
import Bar from 'docs/app/Recipes/Bar';
...

const componentsMap = { Foo, Bar };

...

const Recipe = componentsMap[props.componentName] || () => <div>Not found</div>;

In this case props.componentName can be validated if needed.

in fact there is. With the recent release of React v16.6.0 "lazy code splitting" was introduced. This is how it works, it makes sense to use it together with reacts' 'suspense':

 import React, {lazy, Suspense} from 'react';
 const Recipe = lazy(() =>import(`./docs/app/Recipes/${props.componentName}`));

    function SomeComponent() {
      return (
        <Suspense fallback={<Spinner/>}>
          <Recipe />
        </Suspense>
      );
    }

To handle the case that the component isn't found you can use Error Boundaries. You would wrap your component with it like this:

<ErrorBoundary>
  <Suspense fallback={<Spinner/>}>
    <Recipe />
  </Suspense>
</ErrorBoundary>

Best you read more about it directly on the react docs I linked above.

Related