Can't import SVG in React from Webpack bundle - reactjs

I have a React app that imports components from an own library of components. The library of components is a .js bundle created with Webpack.
The component that I am importing, imports another file which imports SVG files.
Original non-bundled file which imports SVGs:
import React from "react";
import {ShoppingBag32} from '#carbon/icons-react';
import {ReactComponent as OwnIcon} from "../design/icons/OwnIcon.svg";
const foo = {
shop: <ShoppingBag32/>,
ownIcon: <OwnIcon/>,
};
export default foo;
The #carbon/icons-react icons which are precompiled in their own plain JS bundle, work fine. But my own SVG fails to work.
I get:
Warning: React.createElement: 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.
The stack trace of the warning leads to the line in the bundle where ownIcon is defined. You can see that the working icon of the 3rdparty bundle points to a ShoppingBag32 key, and my own SVG points to a ReactComponent key.
var foo = {
shop: /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_2___default.a.createElement(_carbon_icons_react__WEBPACK_IMPORTED_MODULE_0__["ShoppingBag32"], null),
ownIcon: /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_2___default.a.createElement(_design_icons_SolarPanel32_svg__WEBPACK_IMPORTED_MODULE_1__["ReactComponent"], null)
};
Any ideas on how fix the SVG import?
EDIT
I found out a workaround, the img tag can be used with the SVG path as source. But I don't like this solution because I would like the entire SVG markup to be rendered in the DOM instead of an img tag.
import React from "react";
import {ShoppingBag32} from '#carbon/icons-react';
import OwnIcon from "../design/icons/OwnIcon.svg";
const foo = {
shop: <ShoppingBag32/>,
ownIcon: <img src={OwnIcon}/>,
};
export default foo;

If you are using the default create react app, you will need to reference your SVG as follows:
import OwnIcon from "../design/icons/OwnIcon.svg";
The reason it is like import {ShoppingBag32} from '#carbon/icons-react'; for carbon is because it is referenced as an export in that package as opposed to locally which webpack needs a name for the default export of an image.

Related

'Component exception' React Native

I copied my code from an old project of mine so it should be able to work. However, I think when I made the new project it accidentally updated the versions causing it to go thorough an error. This is my code:
import React, {useState} from 'react';
import { Text, View } from 'react-native';
import * as Font from 'expo-font';
import { AppLoading} from 'expo-app-loading';
import {enableScreens} from 'react-native-screens'
//import from libraries
import TabNavigation from './Navigation';
//import TabNavigation
enableScreens();
const fetchFonts = () => {
return Font.loadAsync({
'open-sans': require('./assets/fonts/OpenSans-Regular.ttf'),
'open-sans-bold' : require('./assets/fonts/OpenSans-Bold.ttf')
});
//fetch fonts from the folder so that we can style the title
};
export default function App() {
const[fontLoaded, setFontLoaded] = useState(false);
if(!fontLoaded){
return (
<AppLoading
startAsync ={fetchFonts}
onFinish={()=> setFontLoaded(true)}
/>
);
}
//this will ensure that the font will always be loaded.
return(
<TabNavigation />
);
}
The thing I'm trying to do is run this program and then return Navigation where all the navigation process works.
Here is the error:
Component exception
Element type is invalid: expected a string(for built-in components) or a class/function (for complete components) but got: undefined. You likely forgot to export your component from. the file it's defined in, or you might have mixed up a default and named imports. Check the render method of 'App'.
You don't need to do this fetchFonts method
to load the fonts. If you installed the fonts correctly you just need to put the attribute fontFamily in any style, with the name of the font as its value, for example: textStyle:{ fontFamily: 'Montserrat-Bold' }
For a better understanding you can check this link.
Change your import for app Loading
import AppLoading from 'expo-app-loading';
First, open the terminal and go to the project directory.
Second, run the following command to install the expo-app-loading module:
expo-app-loading
Third, import the module into your App.js file:
import AppLoading from 'expo-app-loading';
Tested on Expo 4.9.0

Testing/Mocking ReactComponent SVG imports with Jest

I am importing SVG's into my component and importing them as components using ReactComponent, for example
import { ReactComponent as D1 } from '../../../assets/images/characteristics/D1.svg';
When I run Jest/Enzyme to test the component, I get the following 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.
Does it appear that I need to mock this? How could I do that?
Although the solution pointed out by #skyboyer worked for me, it showed the following error in the console when running the tests:
<SvgrURL /> is using incorrect casing. Use PascalCase for React components,
or lowercase for HTML elements.
Changing to the following inside the mock file worked for me as a solution:
import React from 'react';
const SvgrMock = React.forwardRef((props, ref) => <span ref={ref} {...props} />);
export const ReactComponent = SvgrMock;
export default SvgrMock;
reference

what does babel-plugin-named-asset-import do

Ok I've looked everywhere and there is no documentation on this Babel module
--babel-plugin-named-asset-import
can someone please explain what it is for and how it works.
Looks like its purpose is to import named exports from non JS/CSS assets. Currently, within the CRA, it appears to only be implemented for svg assets. The goal is to offer another way to import SVGs as React components versus the standard import as a url that needs to be applied to an img element.
Without plugin (default import)
import * as React from 'react';
import logo from './logo.png'; // import file as a url
function Header() {
return <img src={logo} alt="logo" />;
}
export default Header;
With plugin (named import)
import * as React from 'react';
import { ReactComponent as Logo } from './logo.svg'; // import file as a React component
function Header() {
return <Logo />;
}
export default Header;
Update
Going deeper, it appears that the plugin aids in importing svg files in the following ways:
import logo from "logo.svg"; // default import
import { logoUrl } from "logo.svg"; // named import
import { ReactComponent as Logo } from "#svgr/webpack?-svgo!logo.svg"; // ReactComponent import
The CRA specifically targets svg file formats as shown in their test suites. As to whether or not it supports other non-js files, not likely (especially since the babel plugin is only utilized once in the CRA webpack config).
As mentioned in the svgr docs:
SVGR can be used as a webpack loader, this way you can import your SVG directly as a React Component.
This particular plugin aims to import any svg file as the default export.
Please note that by default, #svgr/webpack will try to export the React Component via default export if there is no other loader handling svg files with default export.
Whereas the CRA appears to utilize file/url loader for the default/named exports and specifically maps a ReactComponent named export to the svgr webpack plugin.

Importing React Components and Images with require()

In React, what are the differences between loading an image vs. loading a component using require()?
When I load an image using require("image path"), it works. When I load a React component using require("component") I get an error.
For images in React, You need to require
<img src={require('../assests/logo.png')} />
But it is totally different case with components. You import React components bases on how you exported.
And use ES6 to import and export in reactjs
And there are two types of exports
Named Export
Named exports are useful to export several values at ones. During the import, it is important to use the same name.
// how to export
export function myfunc() {}
// how to import
import { myfunc } from './myfile';
Default export.
Default exports are useful to export only a single value. During the import, able to omit the curly bares and use any name.
// how to export
default export function() {}
// how to import
import anynameyouwant from './myfile';

Importing React Component from bundled file

I have a bunch of React components in a folder and I can import each one individually like this:
import Component1 from '../components/component1/component1';
import Component2 from '../components/component2/component2';
Using the components like this works fine. However, when I bundle all the components using Webpack, the following code returns an empty object when I try to import the component.
import Component1 from '../dist/bundle.js';
I can't render this component. How do I export the components when bundling files?
Simply put you cannot import any modules from a bundle.
You need to include this code inside the source and then bundle it together
Actually you can do that. In my situation I needed react components in backbone application so what I did:
In react exposed components that should be importable by exporting them in entry point (usually index.js) like:
import React from 'react';
import ReactDOM from 'react-dom';
import Header from './components/Header/Header';
export {
React,
ReactDOM,
Header
};
Made my bundle output as library:
output: {
path: /some/path/of/your/lib,
library: 'reactapp',
libraryTarget: 'umd',
filename: `app${jsExtension}`,
},
Then bind name to file path in requirejs config:
reactapp: '/some/path/of/your/lib/app.js'
Then in code I can import that bundle as reactapp like:
define(["reactapp"], function(reactapp) {
// reactapp is object containing React, ReactDOM, Header
});

Resources