Why img path works but not with path.join? - javascript

Using React 16.13.1, I have the follow class:
import React from "react";
const path = require('path');
class Portrait extends React.Component {
render() {
const vikingName = this.props.name.toLowerCase(); // Expected output of "ulf"
return (
<div>
// below img tags go here
</div>
)
}
}
export default Portrait
I'm trying to have a the src prop of the img tags be dynamic, so when the parent component calls it, it passes the name of the viking. The file directory is name-spaced.
This works:
<img src={require('../../../../res/img/ulf/picture.png') />
Error: "Module not found at '../../../../res/img/ulf/picture.png'
<img src={require(path.join('../../../../res/img', vikingName, 'picture.png'))} />
Error: "Module not found at '../../../../res/img/ulf/picture.png'
<img src={require(`../../../../res/img/${vikingName}/picture.png`)} />
When this.props is loading correctly, (this.props.name gives expected output) and all types are String and print the correct, same path, why do the bottom two do not work?

The problem isn't your code itself. The problem lays a little deeper in how Webpack bundles your code.
Webpack bundles your code and assets and creates an entirely new folder structure and/or filenames.
Webpack will turn a folder structure like this:
src
- app.jsx
- components
- - MyComponent.jsx
- assets
- - image.jpg
into:
dist
- main.js
- image.jpg
The whole point about imports/exports is that they are static. Webpack will change all your paths behind the scenes to point towards the newly created bundled path during it's bundling process. That's why your first example works smoothly. That path has been defined before runtime, so before webpack bundles your code.
However...
Dynamic imports which are updated at runtime point towards a path which doesn't exist in your bundled code. That's why it throws an error that it can't find your file. Because there simply is no such file at that location in your bundled code.
Luckily there is a workaround for this issue
How to solve this:
It's possible to access the files path relative to your bundle using require.context. This allows you to parse your relative path to the bundled path and dynamically update your images.
This answer should be able to set you on your way.

Related

Why loading dynamically assets fails on Nuxt v3

I'm experience a weird situation,
I have a "standard" Nuxt v3 project that comes with vite
Works
<img src="~/assets/img/image.png">
<img src="~/assets/video/video.mp4">
Does not work
<img :src="require('~/assets/img/image.png')">
<img :src="require('~/assets/video/video.mp4')">
Note that the image path is the same so it does exist, the error I'm getting is:
Cannot find module '#/assets/img/image.png' Require stack
The docs don't mention anything that has to be done in order to achieve it
Is there anything I should do?
You can't use require with vite and vite is nuxt3 default module bundler
There is two problem:
Nuxt will change assets directory and the file names after the build
aliases not convert to absolute path when you using it dynamically
So you can't do this even:
<img :src="`_nuxt/assets/img/${imageName}`">
it works in dev mode but not after the build.
Solution 1
you can import images and then use them like this:
<script lang="ts" setup>
//#ts-ignore
import image1 from "../assets/images/image1.jpg";
//#ts-ignore
import image2 from "../assets/images/image2.jpg";
//#ts-ignore
import image3 from "../assets/images/image3.jpg";
const images = [ image1, image2, image ]
</script>
Solution 2
I found this way:
<script>
const glob = import.meta.glob("~/assets/images/how-to-use/*", {
eager: true,
});
const getImageAbsolutePath = (imageName: string): string => {
return glob[`/assets/images/how-to-use/${imageName}`]["default"];
};
</script>
You can pass your imageName (don't forget the extension) to this function and get the absolute path.
This way works even after the build.
Solution 3
you can put your images to public directory
learn more: https://nuxt.com/docs/getting-started/assets/#public-directory
The public/ directory content is served at the server root as-is.

Is it possible to use an SVG Sprite map in Storybook react?

I have a react build that has a npm run dev tast and a npm run storybook. My dev task uses the index.ts as you would expect, however storybook (my the looks of things) bundles up packages for each story found.
My problem is that my entry file uses SVG go to create and import a sprite map of SVGs that are in a specific folder
import './styles/main.scss';
import './App';
import svgxhr from '../node_modules/webpack-svgstore-plugin/src/helpers/svgxhr';
const __svg__ = {
path: './media/img/svg/*.svg',
name: 'sprite-store.svg',
};
svgxhr(__svg__);
So when i view my components that reference my svgs like so...
<svg>
<title>{title}</title>
<use xlinkHref={reference}></use>
</svg>
It displays them but only when using the npm run dev task because thats where is gets imported.
Is there anyway i can do with Storybook - been looking online for a while and i cant find anything that helps.
So after a brain slapping day of trying to figure this out i did the following...
In my main webpack config I used SvgStore plugin (spritemap.ts). This configures the nature of how you use the sprite map i.e reference="icon-logo" and the output file.
module.exports = () => {
const plugins = [
//..
new SvgStore({
name: 'sprite-store.svg',
prefix: 'icon-',
}),
//...
];
return plugins;
};
I then created a new TS file with the svgxhr svg import asynchronous into the project.
Now this was all lovely and working in dev but when we included it into storybook it went t*ts up because it uses it's own webpack config.
Through trail and error i decided to do the following...
in ./storybook/webpack.config.js - I injected the spritemap.ts into the entry files to pull in the generated sprite map into the build so we can still use sprite map referencing
config.entry.push(
path.resolve('./src/spritemap.ts'),
);
Inject the svgstore plugin into plugins to generate the spritemap
config.plugins.push(new SvgStore({
name: 'sprite-store.svg',
prefix: 'icon-',
}));
Just thought i would put this there as I couldn't find any answers around.
Hope this helps

Webpack 4 bundles or ignores dynamic imports

I'm using dynamic imports in index.js:
import('./componentA');
import('./componentB');
import('./componentC');
const myIndexVar = 'My Index Var';
index.ts is the entry point in my webpack.config.js.
The result is a single bundle containing all 4 files - index and the 3 components.
My goal is to have each of the files separately in my dist folder, so that index can load the components dynamically on demand at runtime.
i.e. at runtime I'd like to load index.js, and in turn it'll request components a-c via dynamic imports when needed.
Depending on events yields the same results:
document.body.onclick = () => import('./componentA');
If I try something like this it completely ignores the component and doesn't add it in any way to the dist folder:
let componentName = './componentA';
import(componentName);
I tried following this article:
https://webpack.js.org/guides/code-splitting/
Am I misunderstanding what's supposed to happen?
If I'm not on the right track, is there any alternative that can help me reach my goal?

importing images locally in react

I am not understanding how import works in React. I'm currently using create-react-app. I have a folder inside components folder named images:
- src
- components
- images
Insides the components folder I also have a js file named ProductSquare.js.
I am trying to import a picture from images folder using this line
import bumper from './images/bumper1.png';
I have tried changing it to only bumper1.png and many other options. What am I doing wrong here? ( would appreciate a better way to do it in the future).
Is this how programmers import pictures in the real world?
Try using <img src={require('./images/bumper1.png')} />
P.S.
The similar question goes here
If you are using something like Webpack (which create-react-app does) then yes, you can just import the images (and most other file types). They end up getting converted into something (I believe images end up as a data URL).
If your folder structure looks like this:
src
components
ProductSquare.js
images
Then you should be able to use the image like this:
// in ProductSquare.js
import React from 'react';
import bumper from './images/bumper1.png';
class ProductSquare extends React.Component {
render() {
return <img src={ bumper } />;
}
}
If it isn't, double-check that all of the files are named what you think they should be (check for typos and what not).
If everything is correct and it isn't working, try just adding console.log(bumper) before the class. Then:
if it outputs a string that looks like data:blahblah it should work
if it outputs undefined, the image may not be named properly
if it doesn't output, you might not be using ProductSquare correctly
Using the public Folder
Note: this feature is available with react-scripts#0.5.0 and higher.
Create a folder in public folder, for example, 'image', add your photos there.
After that wherever you want you can use image address like this:
<img className="img1" src="/image/pic.jpg" alt=""/>
You Need to use .default after require
<img src={require('./images/bumper1.png').default} />

Accessing webpack/bundled React Class from .html file

Is it not possible to access already 'built' components within the html file that the build is linked to?
I am trying the following -
In bundle.js
var React = require('react');
var ReactDOM = require('react-dom');
var Titles = React.createClass({
render() {
return (
<div>
<h1>{this.props.headerProp}</h1>
<h2>{this.props.contentProp}</h2>
</div>
);
}
});
In my html page -
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/JSXTransformer.js"></script>
<div id="con"></div>
<script type="text/javascript" src="/public/bundle.js'"></script>
<script type="text/jsx">
ReactDOM.render(<Titles headerProp = "Header from props..." contentProp = "Content
from props..."/>, document.getElementById('con'));
</script>
But console outputs React is not defined.
I have even tried to set react globally within the bundle -
window.React = React;
And calling it with window. prefixed but yields the same result.
Because you're mentiong a bundle.js file with a snippet containing commonjs style imports, I'm assuming you're using Webpack.
I have some considerations about your code.
bundle.js file will not expose any module as global. That includes React and any other module you might require inside the bundle. There isn't goint to be window.ModuleName. However, these module are accessible in the Browser via require.js because Webpack will export modules as UMD, that is, they will be accessible through either commonjs or AMD (Require.js).
I'm pretty sure that, if in the entry point of your webpack configuration file, you do something like var React = require("react"); window.React = React, that's actually going to work.
There's a Webpack module meant to expose modules globally (like in window.x) in a more ellegant way than (2) called expose-loader. You should take a look at it.
You should really try to avoid doing what you're trying to do. In your entry.js file (the entry point of your webpack configuration) should be responsible for doing something like ReactDOM.render(..., document.getElementById("#app")). So that, just by including your bundle, the app will render automatically. This is what everybody does.
JSXTransformer.js as well as the <script type="text/jsx"> have been deprecated a long time ago. Now you're supposed to use Babel to compile React.

Categories