Fetching Image from Server with a React Native App - react-native

I'm building a mobile app with a Springboot Rest API backend and a React-Native Frontend.
I'm able to upload a picture from the react-native API to Springboot API and to store it in my postgres database as byte[].
I'm able to retrieve this picture with a simple Get Request tested by Postman.
BUT I'm not able to fetch this image and to display it in my React Native app.
I understood there is a problem to use blob file immediately with react native but I didnt see how to handle it.
Here is my fetch function :
export const getAquariumPicture = async (
aquariumId: string
): Promise<ImageSourcePropType | any> => {
const suffixUrl = "api/downloadAquariumPicture/";
const urlService = urlServer + suffixUrl + aquariumId;
try {
const token = await getData("token");
const response = await fetch(urlService, {
method: "GET",
headers: {
Authorization: token
}
});
return response;
} catch (error) {
console.error(error);
}
};
how can I use this response to pass it as source in an Image balise ?
Here how I'm trying to use the photo :
if (rootStore.tankStore.tankImageState === "pending") {
rootStore.tankStore.storeGetImageTank();
}
const photo = rootStore.tankStore.tankPicture;
console.log("photo = " + photo);
return (
<TouchableOpacity onPress={() => choosePicture()}>
<Image source={cameraIcon} style={styles.icon} />
{photo != null ? (
<Image source={photo} style={styles.photo} />
) : (
<ActivityIndicator />
)}
</TouchableOpacity>
);

In fact I found a solution :
<Image
source={getAquariumImageSource(RootStore.tankStore.tankList[0].id)}
style={styles.photo}
/>
export const getAquariumImageSource = (id: string): ImageSourcePropType => {
return {
uri: `${urlServer}api/downloadAquariumPicture/${id}`,
method: "GET",
headers: {
Pragma: "no-cache",
Authorization: RootStore.memberStore.token
},
cache: "reload"
};
};

Related

Bug at pressable in React Native

im currently developing a log in system .My bug is when i press the pressable button it sends the data to the backend (node server) and recieve a correct status(so the check for the user profile is fine),but it not navigate always to the "Home Page" at first press.The second press make is work but not always with first press,why?
Thanks in advance.
Here are some parts of the code.
Client
//AXIOS request
function getProfile(){
try{
axios.post('http://********/checkData',
{
usernameCheckAXIOS : username,
passwordCheckAXIOS : password,
})
.then(function (response){
setProfile(response.data);
}).catch((error) => {console.log("Log In axios error" + error)})
}
catch(error){
console.log("LogIn try Error" + error);
}
}
{/* Pressable for Sing In */}
<Pressable style = {styles.pressableSignIn} onPress = {() => {getProfile(); if(profile.responseOfProfile == true)
{navigation.navigate('Home')}}}>
<Text style = {styles.signText}>Sign in</Text>
</Pressable>
Node server
//Flag for user response profile
var dataProfile = {
responseOfProfile : false,
dataOfProfile : '',
}
//For data login - Check
app.post("/checkData",(req,res) => {
resultOfData = req.body;
console.log(resultOfData);
database.query(`SELECT id,username,password,image,email from register where username='${resultOfData.usernameCheckAXIOS}' AND password='${resultOfData.passwordCheckAXIOS}'`,(reqQuery,resQuery) => {
if(resQuery != 0){
dataProfile = {
responseOfProfile : true,
dataOfProfile : resQuery[0],
}
res.send(dataProfile);
dataProfile = {
responseOfProfile : false,
dataOfProfile : '',
}
}else{
res.send("false");
}});
})
This part of your code is async (hook state doc)
setProfile(response.data);
You could do it this way:
...
useEffect(() => {
if (profile?.responseOfProfile) {
navigation.navigate('Home')
}
}, [profile])
...
<Pressable style={styles.pressableSignIn} onPress = {() => getProfile())

React Native Workflow, handle 429 erros and data

im looking for a bit of guideness here, im working on a RN app with redux and everytime i enter a new screen on the app, must likely i have a "callinitialData" function inside my useEffect(), using axios to fetch api data to be dispatch() to the redux state.
Everything works but whenever i jump from screen to screen to fast, sometimes i get a 429 error of to many request, so i just setup the redux-persist hoping that would help reduce the amount of request,in my mind thinking that if my api data is equal to my local data, that request wouldnt be necessary to be made.
However it stays the same so i was thinking what would be the best aproach here, on login try to fetch all the data at once > store it at asyncstorage and redux, and fetch that on each screen ?
how would i be able then, if i fetch all the data on login, receive the new data sets from the api in real time?
App functionality -
Edit Profile (img, pass, email, name)
Data Forms (requeast X, submit data, edit forms)
Chat by contacts / create Group chat
Code Example
const ChatScreen = ({ auth: { user }, getChatContacts, chat: { contacts }, navigation }) => {
useEffect(() => {
getChatContacts();
}, []);
const onChatUser = async (_id, name, roomID) => {
console.log(_id, name, roomID, contacts.payload.clone)
navigation.navigate( "Message", {
_id, name, chatRoomId: roomID, allUsers: contacts.payload.clone
});
}
const renderItem = ({ item , index } ) => {
let userName = "";
item.users.map((users, index) => {
let idToCheck = users.toString() !== user._id.toString() ? users : false;
if (idToCheck) {
let getOneUser = contacts.payload.clone.find(x => x._id === idToCheck);
userName += "" + getOneUser.name + ", ";
}
})
return (<TouchableOpacity key={item._id} onPress={() => onChatUser(item._id, item.name, item.roomID)}>
<View style={styles.chatContainer}>
<FontAwesome name="user-circle-o" size={50} color="#000000"/>
<Text style={styles.chatTitle}>{ ((userName).length > 32) ?
(((userName).substring(0,32-3)) + '...') :
userName }</Text>
<FontAwesome name="angle-right" size={25} color="#000000"/>
</View>
</TouchableOpacity>)
};
return (
<SafeAreaView style={styles.container}>
<TextInput
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="always"
placeholder="Search friend"
style={styles.chatsearch}
/>
{contacts ?
(<FlatList
data={contacts.payload.allContact}
renderItem={(item, index) => renderItem(item, index)}
keyExtractor={item => item.id}
style={styles.FlatListContainer}
/>) : (<Text style={styles.FlatListContainer}></Text>)
}
</SafeAreaView>
);
}
const styles = StyleSheet.create({});
ChatScreen.propTypes = {
isAuthenticated: PropTypes.bool,
auth: PropTypes.object,
};
const mapStateProps = state => ({
auth: state.auth,
chat: state.chat
});
export default connect(mapStateProps, {getChatContacts} )(ChatScreen);
Redux Action
export const getChatContacts = () => async dispatch => {
const config = {
header: {
"Content-Type": "application/json"
}
}
try {
const res = await axios.get(API_LINK +"/users/getChatContacts",);
dispatch({
type: GET_CONTACT_CHAT,
payload: res.data
});
} catch (err){
console.log(err)
dispatch({
type: ERROR_FAMILY_PARENT,
payload: { msg: err.response, status: err.response}
});
}
};

undefined is not an object (evaluating 'Amazon.trim') In react native

I am new to react native. And I have created a form. But now its not sending data to server. when I click on Submit button. its keep throwing error like this
undefined is not an object (evaluating 'Amazon.trim')
so whats wrong with my code please help.
here is my code
export default function Add(props) {
const { navigation } = props
const data = props.route.params.data
const amazonD = data.service_details_data[0] ? data.service_details_data[0].amazon_name : ''
const [AmazonError, setAmazonError] = useState([]);
const [Amazon, setAmazon] = useState(undefined);
const validateInputs = () => {
if (!Amazon.trim()) {
setAmazonError('Please Fill The Input')
return;
}
else
{
//+++++++++++++++++++++++++++++++++=submitting form data to api start+++++++++++++++++++++++++++++++++++
{
const data = props.route.params.data
const phpid = data.service_details_data[0] ? data.service_details_data[0].id : ''
AsyncStorage.multiGet(["application_id", "created_by",'leadTagNumber']).then(response => {
fetch('https://xyztech/Android_API_CI/uploaddata/merchant_service_details', {
method: 'POST',
headers: {'Accept': 'application/json, text/plain, */*', "Content-Type": "application/json" },
// We convert the React state to JSON and send it as the POST body
body: JSON.stringify([{ some data}])
})
.then((returnValue) => returnValue.json())
.then(function(response) {
console.log(response)
Alert.alert("File uploaded");
return response.json();
});
});
// event.preventDefault();
}
//+++++++++++++++++++++++++++++++++submitting form data to api end++++++++++++++++++++++++++++++++++++++
Alert.alert("success")
return;
}
};
const handleAmazon = (text) => {
setAmazonError('')
setAmazon(text)
}
return (
<View style={{flex: 1}}>
<ScrollView style={{flex: 1,}} showsVerticalScrollIndicator={false}>
<TextInput
maxLength={30}
placeholder="Amazon *"
style={styles.inputStyle}
onChangeText={(text)=>handleAmazon(text)}
// value={Amazon}
defaultValue={amazonD}
value = {Amazon} />
<Text style={{color :'red'}}>{AmazonError}</Text>
</ScrollView>
<Button
style={styles.inputStyleB}
title="Submit"
color="#FF8C00"
onPress={() => validateInputs()}
/>
</View>
)
}
please ignore this = I am new to react native. And I have created a form. But now its not sending data to server. when I click on Submit button. its keep throwing error like this
Looks like you set Amazon to undefined in useState and then try to access it.
I have also declared such type of error but I will solve it by in the use state I have kept it empty not giving any value for use state then I will use an empty string then it solves.....

how to read file from local storage?

I am uploading image to device's local storage (Android), task is doing with success , now
I am facing issues when I try to read the file from the local storage , it pops up errors
saying that :
invariation Violation object are not valid React child
here is my code to upload the image :
let dirs = RNFetchBlob.fs.dirs;
RNFetchBlob.fs.exists(dirs.PictureDir + "/myfolder")
.then((exist) => {
//console.log(`file ${exist ? '' : 'not'} exists`);
if (!exist) {
try {
const fs = RNFetchBlob.fs;
/
RNFetchBlob.fs.mkdir(dirs.PictureDir + "/myfolder")
.then(() => {
let base64Str = data;
fs.writeFile(dirs.PictureDir + "/myfolder/" + imageName, base64Str, 'base64')
.then(() => {
RNFetchBlob.fs.scanFile([{
path: dirs.PictureDir + "/myfolder/",
mime: 'jpeg/jpg'
}]);
}).catch(() => {
alert("error");
})
here is the path to my image
const path =
dirs.PictureDir+"/myfolder/1718179779c718ba84098b90f6061816fba9f.jpg"
and here my code to access that image , could you please help
<ScrollView keyboardShouldPersistTaps="handled">
<View style={styles.containerWithMargin} >
{RNFetchBlob.fs.readFile(path, 'base64') .then( (data) =>
<Avatar
medium
onPress={this.onSelectPostImage.bind(this)}
source={{uri : data}}
/>
)}
</View>
RNFetchBlob.fs.readFile returns a promise, anything asynchronous should be in ComponentDidMount and from there you can use setState to set imageUrl for example, then in jsx you will have something like this
<View style={styles.containerWithMargin} >
{imageUrl &&
<Avatar
medium
onPress={this.onSelectPostImage.bind(this)}
source={{uri : imageUrl}}
/>
}
</View

React Native Image with 'Bearer' authentication token

In React Native I can use images with <Image source={{uri: 'http://my.server.com/user/id/image/id.png'}} />
The problem is that user images are protected by JWT token which I pass in the header.
Is it possible to somehow include that additional header?
What are my other options?
Thanks!
You can send headers in the source prop.
<Image
source={
{ uri: 'https://yourdomain.com/get-image',
headers: {
Authorization: 'Bearer xyz'
}
}
}/>
you can specify other parameters as well:
ImageSourcePropType.js
For me the answer from blink281 didn't work. It seems this is a common Android problem according to this thread https://github.com/facebook/react-native/issues/25945 and while writing this it's not fixxed. I was looking for another solution and the answer from Samuli Hakoniemi helped me build one, so i wanted to share a fully working example as his Link is not working anymore.
I created an external Component called NetworkImage for this.
import React from "react";
import { StyleSheet, View, Image } from "react-native";
class NetworkImage extends React.Component {
constructor(props) {
super(props);
this.state = {
base64: null,
};
this.style = props.style;
this.imageId = props.imageId;
this.token = props.token;
}
componentDidMount() {
var imageUri = "/auth/diary/image/" + this.imageId;
fetch(imageUri, {
method: "GET",
headers: {
Pragma: "no-cache",
"x-access-token": this.token,
},
redirect: "follow",
})
.then((res) => res.text())
.then((content) => {
let data =
"data:image/jpeg;base64," +
content.substring(1, content.length - 1);
this.setState({
base64: data,
});
});
});
}
render() {
return <Image style={this.style} source={{ uri: this.state.base64 }} />;
}
}
export default NetworkImage;
In this case i had to prepend "data:image/jpeg;base64," because the data i got is raw without the datatype.
import RNFetchBlob from "rn-fetch-blob";
const { config, fs } = RNFetchBlob;
let PictureDir = fs.dirs.PictureDir;
let date = new Date();
let options = {
fileCache: true,
addAndroidDownloads: {
//Related to the Android only
useDownloadManager: true,
notification: true,
path:
PictureDir +
"/Report_Download" +
Math.floor(date.getTime() + date.getSeconds() / 2),
description: "Risk Report Download",
},
};
config(options)
.fetch('GET', url, {
Authorization: token,
})
// when response status code is 200
.then((res) => {
// the conversion is done in native code
console.log(JSON.stringify(res));
alert('report download successfully')
})
// Status code is not 200
.catch((errorMessage, statusCode) => {
// error handling
console.log('errorMessage', errorMessage);
console.log('statusCode', statusCode);
})
that's all. enjoy your coding...
Your options are this one: https://rnplay.org/apps/UowZmw (in order to see the simulator, type document.querySelector('.editor-container').style.width = '50%' in dev console, RNPlay is a bit broken with lengthy content).
Basically what you do is to:
1. serve your images as blobs
2. fetch them with fetch() to the app.
3. use base64 data as content of uri property
Do this in your componentWillMount():
fetch(YOUR_IMAGE_URI, {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + 'TOKEN'
}
}
).then((res) => res.text())
.then((content) => {
this.setState({
base64: content
})
})
You may notice I use res.text() instead of res.blob(). This is because, while writing this, RN doesn't support .blob().
And this goes to render():
return (
<Image style={styles.base64} source={{uri: this.state.base64}} />
)