Static images in the react-native - react-native

How should I work with static images in RN?
Let say I have a component which is call <Flag />.
The Flag can access to assets of many *.png image files (flags).
So, for example, you can ask for Japan's flag like this:
<Flag type="Japan" />
So there exist around 193 flags.
The code for switch bettwen images looks like this:
import USA from '../assets/images/flags/USA.png';
import Japan from '../assets/images/flags/Japan.png';
import Slovakia from '../assets/images/flags/Slovakia.png';
...
const getSource = (type) => {
switch(type) {
case 'Japan':
return Japan;
case 'USA':
return USA;
...
}
}
and render method looks like this:
return (
<Image source={getSource(type)} />
)
Is this approach OK even if there exist 193 flags?
Because if I decided to use images with different screen densities
flag.png
├── flag#2x.png
└── flag#3x.png
then I will have 193 * 3 (597) images files. So it means that bundle for this component will be big right? Exist there a better way how to do that?
What is the best resolution for these images?

let imagesInDisk = {
'Peru': require('/path/to/flag/images/Peru.png'),
'Chile': require('/path/to/flag/images/Chile.png'),
...
}
then in render:
return <Image source={imagesInDisk[countryName]} />
No need to use a different "import" for each image file.
Thanks to Grund for comment!

Related

How to do dynamic image source in react native (expo)?

I have a simple function to display a playing card in my react native (expo) project. It works when testing locally but not when building for a device (APK, etc). From my understanding it is because the string in require can not be dynamic. If that is the case, what is the best way to handle this issue?
Do I have to do a require for all 52 cards and then pick the appropriate variable for the source of that card? or is there a better way?
export default function Card( { cardID, index, onCardClick }) {
const rankIndex = cardID % 13;
const rank = RANKS[rankIndex];
const suitIndex = cardID / 13 | 0;
const suit = SUIT[suitIndex];
let cardImage = require('../../assets/game/cards/'+rank+suit+'.png');
return (
<TouchableOpacity style={[index && index != 0 && styles.cardMargin]} onPress={() => onCardClick(cardID)}>
<Image style={styles.card} source={cardImage} />
</TouchableOpacity>
);
}
Thank you
do something like this:
const cardImages = {
AceSpades: require('../../assets/game/cards/acespaces.png'),
AceClubs: require('../../assets/game/cards/aceclubs.png'),
// etc..
};
you can generate this array from your filesystem by writing a little node script or create it manually.
metro bundler requires that imports are static.
You should make a function that return all the images as an array and then select the index that of specific image that you want.

How to import a directory into a single file component

I am using a photo gallery component in my project. It requires a path to the folder containing the images. I am unable to find a way to do this. I have created an img directory inside of assets, and I'm using the standard Vue CLI 3 scaffolding. I'm able to use a require('path/to/file/name.png'), but what I need is to be able to bring the whole folder in. I'm unable to figure out a way to do this. I even tried placing the images in a folder inside of public, but no luck.
My structure looks like this:
project/public/img
project/src/assets/img/
project/src/components/
I need to get the project/src/assets/img path into a component inside of project/src/components/componentName.vue.
I should also mention that I want to be able to access this directory from the script tag, not the template tag.
You can try something like this:
const requireModule = require.context('../assets/img.',false,/\.png$/)
const images = {}
requireModule.keys().forEach(filename =>
{
const imageName = fileName.replace(/(\.\/|\.png)/g, '');
images[imageName] = requireModule(fileName)
OR
images[imageName] =
{
namespaced: true,
...requireModule(fileName)
}
});
export default images;
Then you can import this file
import photos from 'imagesObject.js'
for (let key in photos) // do whatever you want with the image
Thank you for your answer IVO. That solution did work, but I found another that I wanted to share here for anyone else having a similar problem. The issue I was having was incorrectly referencing the public folder using a relative path instead of BASE_URL. Based on this...
The public Folder Vue CLI Documentation
I created a directory inside of /public then referenced it using process.env.BASE_URL. This solved the problem. Here are the relevant snippets:
Javascript:
data () {
return {
thumbnailDir : process.env.BASE_URL + 'portfolio/'
}
Template:
<transition-group name="thumbnailfade" tag="div">
<img v-for="thumb in filteredImages"
:key="thumb.id"
#click="showLightbox(thumb.name)"
:src="thumbnailDir + thumb.name"
:alt="thumb.alt"
:title="thumb.alt"/>
</transition-group>
<lightbox id="mylightbox"
ref="lightbox"
:images="images"
:directory="thumbnailDir"
:filter="galleryFilter"
:timeoutDuration="5000"
/>

React-native: Image require run before render

I have list of image in project's assets. I call api to get image name.
Then I use that name to show image:
if (name !== null) {
<Image source = {require('../assets/listImage/' + name)}/>
} else {
<Image source = {require('../assets/listImage/abc.png')}/>
}
But app crash because of name is null. It crash even before first screen run (this code is in my 3rd screen in navigation)
UPDATE: full code: https://drive.google.com/open?id=1SlvJ7KRrhmewDxEBgJQ_QD47LUk6sDDb
React-native doesn't support this kind of dynamically image loading.
What i mean is require('../assets/listImage/' + name)} in here name is dyanimcally added while running this application. But as far as i know it is not supported.
You can use below kind of image loading instead of this
if (name !== null) {
switch(name) {
case example1:
<Image source = {require('../assets/listImage/image1')}/>
break;
case example2:
<Image source = {require('../assets/listImage/image2')}/>
break;
...........
}
And especially this name should be switch compatible otherwise use if the condition for it as well. Basically, I need to sat don't dynamically load image while running. Use this kind of code for it
Navigate to this React Native - Image Require Module using Dynamic Names and see answers. You can find another method that can use to load the image. Then select method what u want.

why react native can't require image currently?

<Image source={require(rowData.avatar)} />
error : Unknown name module ‘xxxxxx’
Why can print out the path but can't read pictures?
Try
<Image source={(rowData.avatar)} />
Images cannot use dynamically generated sources. Assuming what you're trying to do is to load an image as part of your package, your code must read:
const avatar = require('./path/to/avatar.jpg');
Only then you can use avatar as your source a follows:
rowData = {
avatar: avatar
}
<Image source={rowData.avatar} />
If you know before hands what images are going to be needed in your app, I suggest that you create an asset file in which you add all your hardcoded require, such as:
// assets.js
return {
avatar1: require('./path/to/file.jpg'),
avatar2: require('./path/to/file.jpg'),
avatar3: require('./path/to/file.jpg'),
}
And then you would construct your rowData as follows:
import avatars from './assets';
rowData = {
avatar: avatars['avatar1']
}
Where you would likely replace avatar1 with a variable containing the key pointing to the avatar you're interested in.
Here is an asset file I used for one of my projects.

Is it possible to require images with a dynamic string and without using Xcode?

This code works:
<Image source={require('./../img/icons/mlb/kc.png')} />
This code does not:
var team = 'kc'; //Retrieved from API
var string = './../img/icons/mlb/'+team+'.png'
<Image source={require(string)} />
Error:
Unhandled JS Exception: Requiring unknown module
"./../img/icons/mlb/kc.png".If you are sure the module is there, try
restarting the packager or running "npm install".
Is there any way to include images dynamically without adding them to Xcode?
According to React's Image documentation, no, it is not possible.
Note that in order for this to work, the image name in require has to be known statically.
You could do that if you knew all of the images in advance and require them beforehand. Then, you could use the team name directly as the image name.
Something like this:
var teamA = require("./../img/icons/mlb/teamA.png");
var teamB = require("./../img/icons/mlb/teamB.png");
var teamC = require("./../img/icons/mlb/teamC.png");
// if you like imports better, then:
// import { default as teamA } from "./img/icons/mlb/teamA.png";
// import { default as teamB } from "./img/icons/mlb/teamB.png";
// import { default as teamC } from "./img/icons/mlb/teamC.png";
const teamByName = (teamName) => {
switch (teamName) {
case "teamA": return teamA;
case "teamB": return teamB;
case "teamC": return teamC;
}
};
const TeamImage = ({team}) => (
<Image source={teamByName(team)} />
);
Edit:
By the way, you don't have to add images through Xcode. You can just place them inside your app folders, like you do with your components. I like to keep mine like this:
ROOT
| index.ios.js
| - app
| |- (other javascript files)
| - img
| |- (image files like png, jpg, gif)