I need to render image tag in a loop based on array length,
My image tag looks similar to this
<Image
style={{width: 50, height: 50}}
source={{uri: 'https://facebook.github.io/react/img/logo_og1.png'}}
//if image fails to load from the uri then load from'./img/favicon.png'
onError={(error) => {console.log('loadinf',error)}}
/>
If error occurs while fetching from Uri i.e 404 error retry for sometime or show default local image.
How to do that in react native?
You put in a loading image in the place of real image using defaultSource property but it is only available to iOS as of this moment. We can achieve the other thing you wrote that it should display a local image in case it couldn't load the image from the internet by the following approach.
Store the original image URI in state.
Use this URI from state in source of the image.
When onError function is called change this state variable to your local image.
Example:
import React, {Component} from 'react';
import {
View,
Image
} from 'react-native';
export default class Demo extends Component {
constructor(props) {
super(props);
this.state = {
image: {
uri: 'something demo'
}
}
}
render() {
return <View>
<Image
source={ this.state.image }
onError={(a) => {
this.setState({
image: require('image.png')
});
}}
style={{ height: 100, width: 100 }}
/>
</View>
}
}
A dirty hack for retrying the image would be something like this:
let counter = 0;
export default class reactNativePange extends Component {
constructor(props) {
super(props);
this.state = {
image: {
uri: 'something demo'
},
failed: false
}
}
render() {
return (
<View style={styles.container}>
<Image
source={ this.state.failed ? require('./image.png') : { uri: 'something demo' } }
onError={(a) => {
if(counter >= 3) {
this.setState({
failed: true
});
} else {
counter++;
this.setState({
failed: true
});
setTimeout(() => {
this.setState({
failed: false
});
}, 1)
}
}}
style={{ height: 100, width: 100 }}
/>
</View>
);
}
}
As I mentioned that this is a dirty hack as the image might flicker during the retry.
Related
SOLVED! : Check the reply below, many thanks to the people who helped! I also had to change the storage rules like this. It's in storage>Rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if true;
}
}
}
I am doing this only for test purposes though. Don't allow everyone to write when you write a real program!
PROBLEM:
This is my cloud storage, how to show this image in my react native app? I tried to do it like this:
<Image style={styles.stretch} source={{
uri: 'gs://mezuniyet2r.appspot.com/images/erkek.jpg',
}}
/>
What am I missing here? It doesn't work like this. Isn't it supposed to work like this? I can't find anything like this at official documentation. Very bad documentation indeed. It doesn't say much. Can someone please help me? And this is the full code:
import React, {Component} from 'react';
import {
Platform,
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
Image,
Button,
StatusBar,
} from 'react-native';
import firebase from '#react-native-firebase/app'
import firestore from '#react-native-firebase/firestore'
import { format } from "date-fns";
import storage from '#react-native-firebase/storage';
class App extends Component {
state = {
tablo: {
adSoyad: "",
yas: "",
dogumTarihi: "",
mezunDurumu: "",
}
}
constructor(props) {
super(props);
this.getUser();
this.subscriber=firestore().collection("tablo").doc
('J6mAav1kjkcjrMupeOqX').onSnapshot(doc => {
this.setState({
tablo: {
adSoyad: doc.data().adSoyad,
yas: doc.data().yas,
dogumTarihi: doc.data().dogumTarihi,
mezunDurumu:doc.data().mezunDurumu,
}
}
)
});
}
getParsedDate(date){
date = String(date).split(' ');
var days = String(date[0]).split('-');
var hours = String(date[1]).split(':');
return [parseInt(days[2]), parseInt(days[1])-1, parseInt(days[0]), parseInt(hours[0]), parseInt(hours[1]), parseInt(hours[2])];
}
getUser= async() => {
const userDocument= await firestore().collection("tablolar").doc
("J6mAav1kjkcjrMupeOqX").get()
console.log(userDocument)
}
render() {
var mezund;
var date = new String(this.state.tablo.dogumTarihi);
var date2=this.getParsedDate(date);
//const reference = storage().ref('erkek.jpg');
//var storageRef = firebase.storage().ref();
//var mountainsRef = storageRef.child('erkek.jpg');
//var mountainImagesRef = storageRef.child('images/erkek.jpg');
//mountainsRef.name === mountainImagesRef.name;
console.log(date2);
//this doesn't work though how do we do this??? it gives undefined.
if(this.state.tablo.mezunDurumu==false){mezund="Mezun Değil"}
else if(this.state.tablo.mezunDurumu=true){mezund="mezun"}
// var dtarih= new String(this.state.tablo.dogumTarihi);
return (
<View style={styles.body}>
<Text style={styles.row}>Ad Soyad: {this.state.tablo.adSoyad}</Text>
<Text style={styles.row}>Yaş: {this.state.tablo.yas}</Text>
<Text style={styles.row}>Doğum Tarihi: {date}</Text>
<Text style={styles.row}>Mezun Durumu: {mezund}</Text>
<Image style={styles.stretch} source={{
uri: 'gs://mezuniyet2r.appspot.com/erkek.jpg',
}}
/>
</View>
);
}
}
const styles=StyleSheet.create({
body:{
padding: 25,
margin:25,
backgroundColor:'orange',
flex: 1
},
stretch:{width:50,height:50,
resizeMode:'stretch',},
row:{
backgroundColor:'#fff',
borderBottomWidth:4,
}
})
export default App
The image path has to be converted into a URL that the <Image/> component can fetch and display. This asynchronous operation is best done once in the component lifecycle and stored in the state.
class App extends Component {
state = {
...,
imageUrl: null,
}
...
async componentDidMount() {
var imageRef = firebase.storage().ref('erkek.jpg');
var imageUrl = await imageRef.getDownloadURL();
this.setState({ imageUrl });
}
render() {
...
<Image style={styles.stretch} source={{ uri: this.state.imageUrl }}/>
...
}
}
You should also make sure that the user has read access to the image. You can set read and write permissions in Firebase Security Rules, see https://firebase.google.com/docs/storage/security/start#sample-rules. If you want the image to be readable by everyone set ".read": true in your rules but be careful not to set write public to prevent unwanted tampering with your project!
I need to loop through my products array then pass the data as props but for my image urls I cant do that and console show me the following:
invalid call at line 21 require(this.props.img)
here is my code:
app.js
export default class App extends React.Component {
state = {
products: [
{
id: 1,
details: 'this is a macbook',
image: '../Images/macbook.jpg',
price: '1000$',
},
{
id: 2,
details: 'this is a PS4 pro',
image: '../Images/ps4pro.jpeg',
price: '500$',
},
{
id: 3,
details: 'this is a beats',
image: '../Images/beats.jpeg',
price: '200$',
},
]
}
showProds = () => {
let prods = [];
for (let i = 0; i <= this.state.products.length - 1; i++) {
prods.push(<Product details={this.state.products[i].details} img={this.state.products[i].image} />)
}
console.log(prods);
return prods;
}
render() {
return <ScrollView>
{this.showProds()}
</ScrollView>
}
}
product component
export default class Product extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<View style={styles.card}>
<View style={styles.prod}>
<Text style={styles.prod_details}>{this.props.details}</Text>
<Image style={styles.img} source={require(this.props.img)} />
</View>
<View style={styles.btn}>
<TouchableOpacity>
<Text style={styles.btn_text}>Delete</Text>
</TouchableOpacity>
</View>
</View >
)
}
}
I'm new to react-native. I also tried to use require in my loop but no luck.
any help would be appreciated.
Change your products.image so their will be in this format:
products: {
...
image: require('.../Images.image.jpeg'),
...
}
And then change image source to this:
<Image style={styles.img} source={this.props.img} />
You cant dynamically require images in react-native. It's a major flaw in react native.
You have two options,
1.Either host the images onany server like cloudinary and send the URI
2.You can require all images at start, and then just include those image components like :
const flower = () => (
<Image source={require('../../)} />
)
const rat = () => (
<Image source={require('../../)} />
)
and (this.props.img == 'flower') ? flower():rat()
Hope you get the gyst. Feel free for doubts
I am new to React Native and struggling a little to get this working. I have realtime database in Firebase which contains 'mechanic' names. I would like to retrieve these names and display them in a list.
I would like to display this data in a list and then execute some function when the user clicks on either name. I thought adding the database data to an array then looping through the array to add it to my FlatList.
The problem now is that when I execute the code, the this.setState({ mechanicsList: mechanicsTemp }); returns an error.
Error
[Unhandled promise rejection: TypeError: this.setState is not a function.
(In 'this.setState({]
* src\screens\FindMechanics.js:28:30 in <unknown>
- node_modules\promise\setimmediate\core.js:37:14 in tryCallOne
- node_modules\promise\setimmediate\core.js:123:25 in <unknown>
- ... 8 more stack frames from framework internals
Full Code
import React, { Component } from 'react';
import { View, Text, SafeAreaView, TouchableOpacity, ScrollView, StyleSheet } from "react-native";
import { Card } from 'react-native-elements'
import firebase from "firebase/app";
export default class FindMechanics extends Component {
constructor(props) {
super(props);
this.state = {
mechanicsList: [],
isDataLoaded: false
}
}
componentDidMount() {
var query = firebase.database().ref("MechanicList").orderByKey();
query.once("value")
.then(function (snapshot) {
let mechanicsTemp = [];
snapshot.forEach(function (childSnapshot) {
// key will be the auth ID for each user
var key = childSnapshot.key;
var mechanicName = snapshot.child(key + '/name').val();
mechanicsTemp.push({ _name: mechanicName, _key: key });
});
mechanicsList = mechanicsTemp;
() => this.setState({ mechanicsList: mechanicsTemp }); // This does not execute it seems - main problem I believe
//this.setState({ mechanicsList: mechanicsTemp }); - This return a warning 'this.setState is not a function'
console.log(mechanicsList); //Prints data as expected
mechanicsTemp.forEach((mechanic) => {
console.log( mechanic._name); //Prints data as expected
});
});
}
render() {
//The Card element is empty - nothing shows.
console.log(this.state.mechanicsList) //This return Array [] which indicates it is empty
return (
<SafeAreaView style={styles.container}>
<ScrollView horizontal={true}>
<TouchableOpacity>
<Card style={styles.card}>
{
this.state.mechanicsList.map((u, i) => {
return (
<View key={i}>
<Text>{u._key}</Text>
<Text>{u._name}</Text>
</View>
);
})
}
</Card>
</TouchableOpacity>
</ScrollView>
</SafeAreaView>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#FFF'
},
paragraph: {
margin: 24,
fontSize: 18,
textAlign: 'center',
},
card: {
flex: 1,
width: '80%',
},
});
Console
Finished building JavaScript bundle in 384ms.
Running application on Android SDK built for x86.
Array []
1st thing, you have mechanics object in state so you need to access it like
console.log(this.state.mechanics)
2nd thing is that you are not updating state variable when you are having data, it should be like following
let mechanicsTemp = [];
snapshot.forEach(function (childSnapshot) {
// key will be the auth ID for each user
var key = childSnapshot.key;
var mechanicName = snapshot.child(key + '/name').val();
mechanicsTemp.push({_name: mechanicName, _key: key});
});
this.setState({ mechanics:mechanicsTemp })
I dunno if you still need help with this or not but I just used your code and I solved this.setState problem with binding. You can either use arrow function or bind your function:
.then(function (snapshot) {
// ..
}.bind(this));
I'm trying to integrate react-native-image-picker and I was able to successfully install it. Now when I try to choose image from gallery in log it does shows that my imageSource has value but it doesn't display. I'm currently running it on iOS simulator. Below is my code
import ImagePicker from "react-native-image-picker";
const options = {
title: "Select a photo",
takePhotoButtonTitle: "Take a photo",
chooseFromLibraryButtonTitle: "Choose from gallery",
quality: 1
};
constructor(props) {
super(props);
this.state = { imageSource: null };
}
addImage = () => {
ImagePicker.showImagePicker(options, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else {
const source = { uri: response.uri };
this.setState({
imageSource: source,
});
console.log("Imagesource=" + JSON.stringify(source));
}
});
}
render() {
return (
<View style={styles.container}>
<Image source={this.state.imageSource} />
</View>
);
}
The console log does print the path to the image I picked from gallery but it just doesn't display. I'm running it on iPad with landscape orientation. Any help is appreciated.
Any Image shown should have fixed width and height.
You can use the Dimensionlike this var { width, height } = Dimensions.get("window"); to use height and width with respect to screen size(Don't forget to import from react-native).
Or you could just go ahead and give your own values to the respective parameters.
I have a react native App which downloads images from external sources and saves them into dedicated local cache folder. Later they get displayed in different views. The images are initially not available in the app bundle.
<Image source={{uri: uri}} resizeMode="contain" style={style} />
I use the URI format for the source input parameter of Image component referring a local folder. I pass width and height as style as well, in fact the image size is always known before drawing. It works fine on iOS but not on Android.
As the documentation says, I should use require('') for local images on Android. This works for static images but not for downloaded on run time.
Has anybody idea how could I solve this issue?
Here is the code:
class CachedImage extends React.Component {
state = {
uri: null
}
async _handleCache(source) {
const path = `${RNFetchBlob.fs.dirs.CacheDir}${sha1(source.uri).toString()}.jpg`;
const exists = await RNFetchBlob.fs.exists(path);
if (exists) {
this.setState({uri: path});
} else {
// encoding the file to preserve proper URL transmition
await RNFetchBlob
.config({path})
.fetch("GET", source.uri, {});
this.setState({uri: path});
}
}
componentWillMount() {
this._handleCache(this.props.source).done();
}
render() {
const { style } = this.props;
if (this.state.uri != null) {
return (
<Image source={{uri: this.state.uri}} resizeMode="contain" style={style} />
);
}
return(
<View style={style}/> //todo: display activity while loading ...
);
}
}
Usage:
<CachedImage
style={{width: _width , height: _height}}
source={{uri: 'http://...'}} />
Import this packages
import { StyleSheet, Image, Dimensions } from 'react-native';
Render image like this
<Image source={{uri: uri}} style={styles.image} />
Add this styles at the bottom
const styles = StyleSheet.create({
image: {
width: Dimensions.get('window').width,
height: Dimensions.get('window').width / 2
}
});
This should work if any issue write that down below!