What the best way to import assets in React JS? - reactjs

I'm currently working on a project with React JS, that contains lots of assets. And actually, I'm wondering what is the best way to import assets between these two methods :
- Creating a JSON file that contains all informations, with their assets paths (eg. mySuperImg: "../assets/img/myImage.jpg" then using the path for my image tags)
- Importing all assets directly in the component (or using the Context API, perhaps?) (eg. import {myImage} from "../assets/myImage.jpg" then using the img tag with that path <img src={myImage} alt="My Image"/>)
My questions : What the best way to import assets ? Is there a speed difference between these two methods ?
Thanks! Take care!

I'd say it depends on you. Whichever way you feel comfortable, you should go for it. Generally, I import the assets in the corresponding file, but the other way can also be used. Instead of json, you could use js approach as well, similar to how we export actions, for eg,
export const ADD_PROJECT = "add_project";
export const FETCH_PROJECTS = "fetch_projects";
You could use something like,
export const IMAGE = require("the location to your image");
To answer if it would take more time, I don't think that there would be any performance issue, as we use a similar approach for the actions most of the times.

it's best to keep the images in assets and use them in your source, if you work with the JSON file then you have to keep track of both of them and it will mess up as you say you have a lot of assets.

Related

Using custom images in components react

I have a react component that is used many times on a products page. Each of the components has its own image. Is there a way to dynamically import an image? For example something like
function productPage({imgPath}){
import img from imgPath
return <img src={img}/>
}
and when I use the component I can do something like
<productPage imgPath="/pathToImage">
I have only come across answers that use require (<img src={require("/pathToImage")} />) However, this method does not work for me. Images only load when I use import. I am not sure why this is (maybe using create-react-app only allows for import?)

React unable to display images from local path

I'm new to react, I'm trying to display images from the src/images folder.
but after inserting the correct path, then also the image is not visible.
Here you can see my img tag path and on the left side folder structure
From the Image you provided, looks like you used require("path...") which is not a good way to do so, its valid tho, but not a good practice, its recommended and is common to use import X from "somewhere" syntax.
I suggest you to use either of these two ways to use images in react.
put images in public folder and use relative links in the href tag, i.e:
<img href="/images/panda.png" alt="cute panda" />
put images inside a folder somewhere inside src folder like you did in the image and import images using ES import syntax, and use imported image variable in {} expression for src prop, i.e:
import PandaImg from "../images/panda.png"
const Panda = ()=>{
return <img src={PandaImg} alt="cute panda" />
}
Here is a CodeSandbox Example may help you further explore the code in broader context.

create-react-app reduce build size: main.[hash].chunk.js is 3MB+ with mostly images

I build my app with yarn build.
It generate a 3.27MB build\static\js\main.8dc5bf7f.chunk.js file:
3.27 MB build\static\js\main.8dc5bf7f.chunk.js
82.79 KB build\static\js\2.61d04f1f.chunk.js
2 KB build\static\css\main.275d97bd.chunk.css
1.93 KB build\static\css\2.8380becc.chunk.css
768 B build\static\js\runtime~main.848c2454.js
The bundle size is significantly larger than recommended.
Consider reducing it with code splitting
You can also analyze the project dependencies
I used source-map-explorer to analyze the file and it show:
So the file include all of the images of my entire app.
But funny thing is the build still have a static/media directory which has all of those images in .png form. And apparently the app still load those png files to show the images.
So what are those things in the js file? I wonder if they're even used at all?
Please:
Explain to me why all of the images in my entire application is included in my logic javascript file.
Is there anyway to remove them? I need to increase page load. 4MB at initial load is unbelievable.
This is a tough problem that most engineers have as the app grows larger.
In your case you're importing a lot of small PNGs that get base64 encoded due to CRA's webpack setting. You can override that behavior with a library like react-rewired. While it's a bit more involving process, I'd recommend that over maintaining an external image repository. It's easier to test and use in a development environment. Especially when offline.
I also see that most of your views are bundled together in main.js.
The secret to cutting down the bundle size is in leveraging code splitting and lazy loading.
Here's the link to the video that I put together for this: https://www.youtube.com/watch?v=j8NJc60H294
In a nutshell, I suggest the following patterns:
Code-split routes (if any)
This is commonly achieved by using React.lazy()
import React, { Suspense, lazy } from 'react';
// import MyRoute from 'routes/MyRoute' - we are replacing this import
const MyRoute = lazy(() => import('routes/MyRoute'));
const Loading = () => <div>Loading route...</div>;
const View = () => (
<Suspense fallback={Loading}>
<MyRoute />
</Suspense>
);
Code-split low priority elements
Every page has Critical Path content. It's the content your visitors want to experience first. It's the primary purpose of a view. It's usually in the above the fold area (the part of the view they see when the page loads, without any scrolling).
However, that part can also be dissected based on priority. A Hero is a good critical path example, so we should prioritize it. A Hamburger menu is a great element to de-prioritize because it requires interaction to be viewed.
Anything below the fold can be de-prioritized too. A great example of this is Youtube comments. They only get loaded if we scroll down sufficiently.
You can follow the same principle as above to prioritize critical path content:
import React, { Suspense, lazy } from 'react';
import Hero from 'component/Hero'
const Burger = lazy(() => import('components/Burger'));
// The fallback should be used to show content placeholders.
// It doesn't have to be a loading indicator.
const Loading = () => <img src="path/to/burger/icon" alt="Menu"/>;
const View = () => (
<main>
<Hero />
<Suspense fallback={Loading}>
<Burger />
</Suspense>
</main>
);
Create abstract components for libraries
Following the principles above, I suggest creating simple abstract components for every library. Let's say you need animations and you use Framer Motion. You can import it in one place and use everywhere.
Many libraries ship with named exports and lazy() doesn't support that. I wrote a very simple library react-lazy-named that helps with that.
// AnimatedDiv.js
import React, { lazy } from 'react';
// This is similar to
// import { motion } from 'framer-motion';
// const Div = motion.div;
// Also, we hint webpack to use resource preloading (think service worker goodness)
const Div = lazy(() => import('framer-motion' /* webpackPreload: true */), 'motion.div'));
// Use regular div as a fallback.
// It will be replaced with framer-motion animated Div when lazy-loaded
const AnimatedDiv = (props) => (
<Suspense fallback={<div>{props.children}</div>}>
<Div {...props} />
</Suspense>
);
If you have any questions or need and help with this you can reply here or in the comments of the aforementioned video on Loading React apps in under 3 seconds
Basically, anything you import in your app and handle bundling it with webpack would be considered as a dependency.
so: import img from path/to/img would make it a dependency, thus, included in your bundle, that's what you want to escape.
There are two possible scenarios to work-around this:
Stop importing images, make them available/hosted in a CDN such like AWS S3.
Apply a Move Statics Out approach, which is about moving all directories for static files out of the bundle, make them available in statics dependent folder and start using relative links instead, I would suggest copy-webpack-plugin.
For me, I would go with number 1.
Small image files will be loaded into your code when build with url-loader to reduce number of image requests. Large image files will be copied to build folder with file-loader.
You can use webpack option IMAGE_INLINE_SIZE_LIMIT to control which size of images will be put into the bundled js.
Create file .env.production at the root of project dir(same level with file package.json)
Add IMAGE_INLINE_SIZE_LIMIT=0 to this file, this tells webpack do not put any images into the js as base64 data. the default value is 10000, which will put images large than 10kb into js file.
To reduce to chunk js file size, take a look at Code splitting here: https://reactjs.org/docs/code-splitting.html

Dynamically Importing Images Based on a Variable

I have a requirement to select, from a local source, an image based on the value passed back from the REST API I am using. For example:
//Psuedo-call from the API
var imageIdToSelect = response.data.imageId
//Then later in the render()
<img src={ baseUrl + imageIdToSelect } />
I have a solution to this, which is to use require() as that allows me to append the url as such:
<img src={ require(baseUrl + imageIdToSelect) } />
This works fine, however, I am using a Microsoft TSLINT setup that does not allow require() over the prefered import at the top of the file "no-require-imports".
I know I am not meant to let linting tools control my work to the point where I am just blindly following rules. So my question is two-fold:
Why is it frowned upon to use require() in such a way. One reason I could think of is that if all of the external files/resources are declared at the top of the file, then you don't have to look through the source to find them hidden in functions.
What would the import x from './' solution look like here? I have seen people creating index.js and index.d.ts files inside their image folders to import and export all the images inside but that seems a tad extraneous.
Edit: I also have just realised that using require() with a non-literal string is a violation of my ts-linting too.
Thanks in advance

Avoiding a long list of imports in React

For a SET game that I am creating in React (using Create React App), I need to import 81 image files representing the 81 possible cards used in the game. With the help of a Python script, I created this extremely long list of imports
import i0000 from './assets/0000.png'
import i0001 from './assets/0001.png'
[...]
import i2221 from './assets/2221.png'
import i2222 from './assets/2222.png'
and since I will need to reference those variables using strings representing each card, I have this object:
const refs = {
'0000': i0000,
'0001': i0001,
[...]
'2220': i2220,
'2221': i2221,
'2222': i2222
};
The good thing is that now I have all the card images pre-loaded to be called easily with
<img src={refs[card]} />
But the bad thing is I have 164 lines of ridiculous code that make it work. I'm wondering if there is a better way to pre-cache and reference a directory full of images.
Move all the images to your public folder, say public/cards.
Then you can reference them with <img src={`cards/${card}.png`} /> without the need for any import statement.
Webpack can no longer warn you if the referenced images are missing though, so just make sure they are there.
Two approaches:
First, you eliminate manually creating the refs object by struturing your code like this:
export i0000 from './assets/0000.png'
export i0001 from './assets/0001.png'
And where you want to use these assets:
imports * as refs from './assets'
<img src={refs[card]} />
Secondly, you could use a bundling system that supports dynamic requires e.g. https://webpack.github.io/docs/context.html#dynamic-requires

Resources