It would be great if the code could be simplified using dynamic imports. I haven't tried this myself but if the image names could be put into an array then something like the following might work, where the exported images are set as properties on an object that can be exported. When this SO answer to Create a loop to import files dynamically in React was written only Chrome and Safari supported that feature but since then it appears that Edge and Firefox now offer support as well.
const images = {};
const nums = [1, 2];
for (const format of ['png', 'webp']) {
for (const num of nums) {
const key = `image${num}${format === 'webp' ? 'webp' : ''}`;
images[key] = await import( `./assets/image-${num}.${format}`);
}
}
But according to the Hacks blog post by Jason Orendorff ES6 In Depth: Modules
For a dynamic language, JavaScript has gotten itself a surprisingly static module system.
- All flavors of
import
and export
are allowed only at toplevel in a module. There are no conditional imports or exports, and you can’t use import
in function scope.
- All exported identifiers must be explicitly exported by name in the source code. You can’t programmatically loop through an array and export a bunch of names in a data-driven way.
So the function code above could be exported in an async function:
export async function getImages() {
const images = {};
//...set keys on images
return images;
}
Then that function can be imported:
import { getImages } from './assets.js';
But to use that function it would need to be run in an async function:
(async () => {
const images = await getImages();
//use images
})();
Other approaches are listed in answers to this similar post on freecodecamp.org - e.g. from this answer by Dan Couper:
One pretty simple solution:
// images.js
const images = [
{ id: 1, src: './assets/image01.jpg', title: 'foo', description: 'bar' },
{ id: 2, src: './assets/image02.jpg', title: 'foo', description: 'bar' },
{ id: 3, src: './assets/image03.jpg', title: 'foo', description: 'bar' },
{ id: 4, src: './assets/image04.jpg', title: 'foo', description: 'bar' },
{ id: 5, src: './assets/image05.jpg', title: 'foo', description: 'bar' },
...etc
];
export default images;
// MyComponent.js
import images from './images'
//...snip
{ images.map(({id, src, title, description}) => <img key={id} src={src} title={title} alt={description} />)
You’re basically writing a database of images that match the ones in assets.
Then you can be clever in images.js to pull the correct prefix based on the environment if you want. There will be a WebPack plugin that kinda does this as well if you have a google around (you want it to take a directory of files and generate an iterable set of paths to them).
The import
as described is more for importing one-off images (logos etc) rather than importing lots of images from a directory.
Regarding the pseudocode at the end of the post:
export const toWEBP = (src) => {
const location = src.split(".")
return `${location}.webp`
}
if src
contained an extension (e.g. png
) then webp
would be appended to that existing extension. One option would be to use pop
to remove the existing extension and also push webp
onto the array before using it in the return value.
[“image-1”, “image-2”...]
? \$\endgroup\$create-react-app
as a starting point. And as far as I'm concerned they don't expose webpack settings \$\endgroup\$