1

In my application I got 8 svg files which I want to import dynamically. I have tried the following:

First I created a Floor component that has one name prop. This is the name of the floor that the user wants to see. In my dynamic import, the path to the svg is specified but it always seems to return undefined.

I know the path to the svg is correct because if I fill in a wrong path, React complains it can't find the svg file.

import React, { useEffect, useState } from "react";

const Floor = ({ name }) => {

  const [floor, setFloor] = useState(null);

  useEffect(() => {

    /*
    This is where it goes wrong. If I log the result It shows 'undefined'.
    */

    import(`./floors/${name}.svg`)
      .then((module) => {
        setFloor(module);
      })
      .catch((error) => {
        console.error(`Icon with name: ${name} not found!`);
      });
  }, [name]);

  const renderFloor = () => {
    if (!floor) return null;

    const Floor = floor.ReactComponent;

    return <Floor />;
  };

  return <>{renderFloor()}</>;
}

export default Floor;

And this is how I import the Floor

import Floor from './Floor'

const Floorplan = () => {

  return (
    <Floor name="floor-4" />
  )
}

export default Floorplan;

I have found something similar but I still have the same error 'undefined'

These are the errors Im getting.
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

And if I log the dynamic import response:

Object { loaded: Getter, id: Getter, exports: undefined, webpackPolyfill: 1 } Icon.js:14

Ruben
  • 515
  • 1
  • 5
  • 19
  • What happens when you import any of your svgs statically (with `import { ReactComponent } from './floor/name.svg'`)? – aleksxor May 25 '21 at 15:00
  • @aleksxor In my FloorPlan.js component? If I do that the svg loads succesfully. – Ruben May 25 '21 at 16:07

2 Answers2

2

The below worked for me. I had my svgs in a folder inside src, and was using the companyName prop for the svg filename. I also used a regex to format companyName to all lower case and no space (like the svg filenames)

  const [iconUrl, setIconUrl] = useState(false)

  const { companyName } = job.companyByCompany

  useEffect(() => {
    iconFetch(companyName.toLowerCase().replace(/\s+/g, ''))
  }, [companyName])

  const iconFetch = async (iconName) => {
    await import(`../assets/logos/${iconName}.svg`)
    .then(data => setIconUrl(data.default))
    .catch(err => console.log(err))
  }

I could then access the svg's via the src attribute in an img tag.

<img src={iconUrl} alt={`${companyName} svg`}/>
DanB-Web
  • 33
  • 1
  • 6
0

I believe the problem is webpack cannot determine what your ${name}.svg will look like in runtime and does not include them inside any chunk. To force this behavior you should include some magic comments inside your dynamic import. Something like:

  ...
  useEffect(() => {
    import(
      /* webpackInclude: /\.svg$/ */
      /* webpackChunkName: "svg-imgs-chunk" */
      /* webpackMode: "lazy" */
      `./floors/${name}.svg`)
      .then((module) => {
        setFloor(module);
      })
  ...

aleksxor
  • 7,535
  • 1
  • 22
  • 27
  • Have tried this, still no luck. didn't know about magic comments. Will do some research. – Ruben May 25 '21 at 16:41