Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 872
rubentd Avatar asked Nov 15 '18 19:11

rubentd


People also ask

How do I import component dynamically into React?

In React, dynamically importing a component is easy—you invoke React. lazy with the standard dynamic import syntax and specify a fallback UI. When the component renders for the first time, React will load that module and swap it in.

What is default import React?

The React is imported as default import and renders as named import.

Do I need to import React in every file?

1 Answer. Show activity on this post. You no longer need to import React from "react" . Starting from the release 17 of React, JSX is automatically transformed without using React.

How do you lazy load components in React?

lazy(() => import('./OtherComponent')); This will automatically load the bundle containing the OtherComponent when this component is first rendered. React. lazy takes a function that must call a dynamic import() .


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.

like image 80
Estus Flask Avatar answered Oct 16 '22 08:10

Estus Flask


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.

like image 33
Fabian Hinsenkamp Avatar answered Oct 16 '22 07:10

Fabian Hinsenkamp