Dynamically Importing Components in React - "ReferenceError: require is not defined"

328 Views Asked by At

Brief overview of what I am doing

I am building a generic Icon component in React which will allow me to dynamically import and instantiate material icon SVG components. I am using a Vite React + TS + SWC template. I have the dynamic import working through implementing this Vite plugin:
https://github.com/vite-plugin/vite-plugin-dynamic-import

My solution is basically a copy paste of this (refactored for Typescript):
https://dev.to/barrbozzo/the-right-way-to-use-svg-icons-with-react-30o3

Material SVG icons library I am using:
https://mui.com/material-ui/icons/#usage\ @mui/icons-material

tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,
    "allowJs": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,

    /* Type fixing */
    "types": [ "node" ],

  },
  "include": ["src"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

Icon.tsx:

import { useEffect, useState } from "react";

type Props = {
    name:string;
    typography?:string;
    color?:string;
}

function Icon(props:Props) {
  const { name, ...otherProps } = props;

  /* Use state hook to store icon module value */
  const [iconModule, setIconModule] = useState<any>(null);

  useEffect(() => {
    /* Use dynamic import to get corresponding icon as a module */

    import (`@mui/icons-material/${name}`)
      .then((module) => {
        /* Persist data in state */
        setIconModule(module);
      })
      .catch((error) => {
        /* Do not forget to handle errors */
        console.log(error)
        console.error(`Icon with name: ${name} not found!`);
      });
  }, [ name /* update on name change */ ]);

  const renderIcon = () => {
    if (!iconModule) return null;

    /* Equal to: import { ReactComponent as Icon } from "./path/to/icon.svg" */
    const Component = iconModule.ReactComponent;

    return <Component {...otherProps} />;
  };

  return <>{renderIcon()}</>;
}
export default Icon;

What's Happening

When the JS runtime hits import (`@mui/icons-material/${name}`) it triggers the import successfully (thanks to the plugin). For my tests I am importing AccessAlarm.js from @mui/icons-material. As AccessAlarm.js is executed, it hits a require() statement on line 4 and throws ""ReferenceError: require is not defined":

>Error shown in console<

Why I suspect this is happening

Well, clearly... require is not defined! But why isn't it defined in the given context? I suspect it's something to do with the way Vite performs bundling. Or perhaps, just my lack of understanding on what this implies about the runtime environment state. According to their docs they perform "prebundling" with esbuild: https://vitejs.dev/guide/dep-pre-bundling.html

Going back to the example I am working from, the author describes the bundling process (his example uses webpack) as follows:

import(./icons/${name}.svg) will cause every .svg file in the ./icons directory to be bundled into the new chunk. At run time, when the variable name has been computed, any file like star.svg will be available for consumption...

Of note is that his example is working with static .svg files while the specific @mui library I am trying to use is generating/returning JSX representations. I think where I've run off course is that I am trying to instantiate JSX objects in runtime that should have been transpiled by the bundler. If that is the case, what's the best solution here? Reroute the control flow somehow to get the JSX icons transpiled (but then how is it dynamic)? Or is there an alternative way to reference these .svg's from @mui?

0

There are 0 best solutions below