How to upload file in React native with imagepack - javascript

I would like to upload an image on the server, it appears in the preview, but it is sent in request.body, but I would like it to be sent in request.file. What am I doing wrong? I send it to the web in react and I have no problems, only in native react. When I send in request.body, the file goes as [Object] [Object], but I would like to extract the path and image from the file sent via mobile.
index.js
import React, { useState, useEffect } from "react";
import { useNavigation } from "#react-navigation/native";
import * as ImagePicker from "expo-image-picker";
import Constants from "expo-constants";
import {
View,
Image,
TextInput,
Text,
TouchableOpacity,
Picker,
ScrollView,
Alert,
} from "react-native";
import api from "../../services/api";
import logoImg from "../../assets/icon.png";
import styles from "./styles";
export default function NewProvider() {
const [name, setName] = useState("");
const [avatar, setAvatar] = useState(null);
const [pickerVisible, setPickerVisible] = useState(false);
const [loading, setLoading] = useState(false);
async function handleSubmit() {
try {
setLoading(true);
let localUri = avatar.uri;
let filename = localUri.split("/").pop();
let match = /\.(\w+)$/.exec(filename);
let typeImg = match ? `image/${match[1]}` : `image`;
let formdata = new FormData();
formdata.append("avatar", {
type: typeImg,
uri: localUri,
name: filename,
});
formdata.append("name", name);
await api.post("/providers", formdata, {
headers: {
"Content-Type": "multipart/form-data",
},
});
} catch (err) {
console.log(err)
}
setLoading(false);
}
async function getPermissionAsync() {
if (Constants.platform.ios) {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if (status !== "granted") {
alert("Sorry, we need camera roll permissions to make this work!");
}
}
}
async function pickImage(e) {
try {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
base64: true,
});
if (!result.cancelled) {
setAvatar(result);
}
} catch (err) {
console.log("erro", err);
}
}
useEffect(() => {
getPermissionAsync();
}, []);
return (
<View style={styles.container}>
<View style={styles.header}>
<Image style={styles.logoImage} source={logoImg} />
</View>
<Text style={styles.loginTitle}>Cadastrar prestador de serviço</Text>
<View style={styles.login}>
<ScrollView
vertical
showsHorizontalScrollIndicator={false}
contentContainerStyle={{ paddingHorizontal: 20 }}
>
<TextInput
placeholder="* Nome do(a) prestador(a)/empresa"
onChange={(e) => setName(e.target.value)}
style={styles.inputForm}
value={name}
/>
<View
style={{ flex: 1, alignItems: "center", justifyContent: "center" }}
>
<TouchableOpacity style={styles.getImage} onPress={pickImage}>
<Text style={styles.imageText}>Selecione uma imagem</Text>
</TouchableOpacity>
{avatar && (
<Image
source={{ uri: avatar.uri }}
style={{ width: 200, height: 200, marginTop: 20 }}
/>
)}
</View>
<TouchableOpacity style={styles.action} onPress={handleSubmit}>
<Text style={styles.actionText}>
{loading ? "Enviando..." : "Cadastrar"}
</Text>
</TouchableOpacity>
</ScrollView>
</View>
</View>
);
}

Bye looking at your code I think there is a better way of doing it.
First I recommend creating a function to handleImageUpload
as shown below
handleUploadPhoto = () => {
fetch("http://localhost:3000/api/upload", {
method: "POST",
body: createFormData(this.state.photo, { userId: "123" })
})
.then(response => response.json())
.then(response => {
console.log("upload succes", response);
alert("Upload success!");
this.setState({ photo: null });
})
.catch(error => {
console.log("upload error", error);
alert("Upload failed!");
});
};
This will force react native to send Image as request.file not request.file and if that all goes well it check your console and see if it says SUCSESS!
And to call it, you could use something like this
<Button title="Upload" onPress={this.handleUpload} />
Now remember THIS IS AN EXAMPLE change it to your needs.

Related

React Native Google / Facebook Login

I'm quit new in React native but I would like to set up Google login but what I did didn't seem to work.
(That will be a long post haha )
For Google Login:
I tried several tutorials on YouTube (this one for example: https://www.youtube.com/watch?v=DN9dQ_6ezvA)
"Request is missing required authentication credential. Expected OAuth
2 access token, login cookie or other valid authentication credential.
See
https://developers.google.com/identity/sign-in/web/devconsole-project."
I tried a lot of thing but it's really hard for me. So if someone could give me some help? Thanks you in advance guys, I continue the research on my side
Here is my code:
WebBrowser.maybeCompleteAuthSession();
export default function GoogleFunc({largeur}) {
const fontRegular = require("../../assets/Manrope-Regular.ttf");
const fontBold = require("../../assets/Manrope-Bold.ttf");
const [fontsLoaded] = useFonts({
ManropeRegular: fontRegular,
ManropeBold: fontBold,
});
const [accessToken, setAccessToken] = React.useState();
const [userInfo, setUserInfo] = React.useState();
const [request, response, prompAsync] = Google.useIdTokenAuthRequest({
androidClientId:
"ANDROID_CLIENT_ID",
iosClientId:
"CLIENT_CLIENT_ID",
clientId:
"CLIENT_ID",
});
React.useEffect(() => {
if (response?.type === "success") {
console.log("response: ", response.params);
setAccessToken(response.params.id_token);
accessToken && fetchUserInfo();
}
}, [response, accessToken]);
async function fetchUserInfo() {
console.log("test222");
let response = await fetch("https://www.googleapis.com/userinfo/v2/me", {
headers: {Authorization: `Bearer ${accessToken}`},
});
const useInfo = await response.json();
console.log("useinfo: ", useInfo);
setUserInfo(useInfo);
}
const ShowUserInfo = () => {
if (userInfo) {
console.log("userinfooofofof: ", userInfo);
return (
<View style={styles.userInfo}>
<Image source={{uri: userInfo.picture}} style={styles.profilePic} />
<Text>Welcome {userInfo.name}</Text>
<Text>{userInfo.email}</Text>
</View>
);
}
};
return (
<View style={styles.container}>
{userInfo && <ShowUserInfo />}
{userInfo == null && (
<>
<TouchableOpacity
style={[styles.googleBtn, {width: largeur}]}
disabled={!request}
onPress={() => {
prompAsync();
}}
>
<Image
style={{
height: height * 0.024,
width: width * 0.057,
left: width * 0.035,
top: height * 0.013,
}}
source={require("../../assets/images/google-logo.png")}
/>
<Text
style={{
textAlign: "center",
fontFamily: "ManropeSemiBold",
fontSize: 17,
color: "#262626",
bottom: height * 0.01,
}}
>
Continuer avec Google
</Text>
</TouchableOpacity>
</>
)}
</View>
);
}
For Facebook:
I tried to set up the login with / without firebase but I already got the same issue : an error ...
I'm trying to solve it since last week but I don't understand how can i fix these problems ...
Here's my code without Firebase:
import React, {useState} from "react";
import {
StyleSheet,
Text,
View,
Image,
TouchableOpacity,
ActivityIndicator,
} from "react-native";
import * as Facebook from "expo-facebook";
export default function FacebookFunc() {
const [isLoggedin, setLoggedinStatus] = useState(false);
const [userData, setUserData] = useState(null);
const [isImageLoading, setImageLoadStatus] = useState(false);
async function faceookLogIn() {
try {
await Facebook.initializeAsync({
appId: "APP ID",
});
const {type, token, expirationDate, permissions, declinedPermissions} =
await Facebook.logInWithReadPermissionsAsync({
permissions: ["public_profile"],
});
if (type === "success") {
// Get the user's name using Facebook's Graph API
const response = await fetch(
`https://graph.facebook.com/me?access_token=${token}`
);
Alert.alert("Logged in!", `Hi ${(await response.json()).name}!`);
} else {
// type === 'cancel'
}
} catch ({message}) {
alert(`Facebook Login Error: ${message}`);
}
}
logout = () => {
setLoggedinStatus(false);
setUserData(null);
setImageLoadStatus(false);
};
return isLoggedin ? (
userData ? (
<View style={styles.container}>
<Image
style={{width: 200, height: 200, borderRadius: 50}}
source={{uri: userData.picture.data.url}}
onLoadEnd={() => setImageLoadStatus(true)}
// GERER QUAND MARCHERA
/>
<ActivityIndicator
size={"large"}
color={"#0000ff"}
animating={!isImageLoading}
style={{position: "absolute"}}
/>
<Text style={{fontSize: 22, marginVertical: 10}}>
Hi {userData.name}!
</Text>
<TouchableOpacity style={styles.logoutBtn} onPress={logout}>
<Text style={{color: "#fff"}}>Logout</Text>
</TouchableOpacity>
</View>
) : null
) : (
<View style={styles.container}>
<TouchableOpacity style={styles.loginBtn} onPress={faceookLogIn}>
<Text style={{color: "#fff", textAlign: "center"}}>
Login with facebook
</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {},
loginBtn: {
backgroundColor: "#4267b2",
paddingVertical: 10,
borderRadius: 10,
width: "95%",
},
logoutBtn: {
backgroundColor: "grey",
includeFontPadding: 10,
paddingHorizontal: 20,
borderRadius: 20,
position: "absolute:",
bottom: 0,
marginBottom: 200,
},
});
And here my code when I trying with Firebase:
The error:
Possible Unhandled Promise Rejection (id: 0):
TypeError: null is not an object (evaluating 'LoginManager.logInWithPermissions')
The code:
import React from "react";
import {LoginManager, AccessToken} from "react-native-fbsdk-next";
import {
StyleSheet,
View,
Text,
Image,
TouchableOpacity,
Button,
} from "react-native";
import {
getAuth,
signInWithCredential,
FacebookAuthProvider,
} from "firebase/auth";
import "expo-dev-client";
import app from "./firebaseSetup";
export default function App() {
const SignInWithFB = async () => {
const result = await LoginManager.logInWithPermissions([
"public_profile",
"email",
]);
if (result.isCancelled) {
throw new Error("User cancelled login");
}
const data = await AccessToken.getCurrentAccessToken();
if (!data) {
throw new Error("Something went wrong obtaining access token");
}
const auth = getAuth(app);
const credential = FacebookAuthProvider.credential(data.accessToken);
const user = await signInWithCredential(auth, credential);
console.log(user);
};
return (
<View>
<Button title="Sign in with facebook" onPress={SignInWithFB}></Button>
</View>
);
}
const styles = StyleSheet.create({});
I initialize my firebaseSetup like this:
import {initializeApp} from "firebase/app";
// Initialize Firebase
const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: "",
};
const app = initializeApp(firebaseConfig);
export default app;
I continue my research by my side but if someone could help me with this problem (which is probably stupid), thanks you in advance...
PS: There is the error for Facebook without firebase:
Facebook Login Error: undefined is not an object (evaluating'_ExponentFacebook.default.InitializeAsync')
You have to use expo auth session to implement oAuth in Expo
import React,{useState} from 'react';
import { useNavigation } from "#react-navigation/native";
import Fontisto from '#expo/vector-icons/Fontisto';
import { TouchableOpacity,StyleSheet, Alert } from 'react-native';
import * as WebBrowser from 'expo-web-browser';
import * as Facebook from 'expo-auth-session/providers/facebook';
import {makeRedirectUri, ResponseType } from 'expo-auth-session';
import { useDispatch } from "react-redux";
WebBrowser.maybeCompleteAuthSession();
export default function FacebookAuth(){
const [isLoading, setIsLoading] = useState(false);
const navigation = useNavigation();
const dispatch = useDispatch();
const [request, response, promptAsync] = Facebook.useAuthRequest({
responseType: ResponseType.Token,
clientId: 'YOUR_CLIENT_ID',
scopes: ['public_profile', 'email'],
redirectUri: makeRedirectUri({ useProxy: true })
},
{ useProxy: true });
React.useEffect(() => {
if (response?.type === 'success') {
setIsLoading(true);
const accessToken = response.authentication.accessToken;
console.log(accessToken);
}
}, [response]);
return(
<>
<TouchableOpacity disabled={!request} style={styles.socialLoginButton}
onPress={() => {
promptAsync();
}}
>
<Fontisto name='facebook' color={'white'} size={25}/>
</TouchableOpacity>
</>
)
}
const styles = StyleSheet.create({
socialLoginButton:{
margin:3,
backgroundColor:'blue',
padding:10,
borderRadius:50,
width:50,
height:50,
justifyContent:'center',
alignItems:'center',
margin:10,
}
})
Here's the code for it, you have to configure it according to your needs.
here's the link for expo auth session. - here
For login with Google, I think the problem lies with this code:
React.useEffect(() => {
if (response?.type === "success") {
setAccessToken(response.params.id_token);
accessToken && fetchUserInfo();
}
}, [response, accessToken]);
You use the id_token from the response, and you should be using the access token. Also, I think you could split this into two useEffect methods:
React.useEffect(() => {
if (response?.type === "success") {
setAccessToken(response.params.access_token);
}
}, [response]);
React.useEffect(() => {
fetchUserInfo();
}, [accessToken]);
The other effect will trigger only when the state of accessToken changes. If it's possible that you change the state to an empty value then you should retain the null check.
As for Facebook and Firebase issues — are you sure you followed all the required setup for these libraries? Just skimming through the documentation shows that there are quite a few things that should be configured before you can call the libs. From your error messages, it seems that the libraries are not configured and initialized properly.

Network Error when trying to connect custom API to react app

I have a network error in Expo React with custom built API in Node.js. I've tried everything on the internet but nothing works. I'm new, so it must be a simple fix.
I think the API is the problem, but nothing works either side.
Here is the code to my custom built API
const express = require("express");
const app = express();
const PORT = 8080;
const res = require("res");
const req = require("request");
const { createWorker} = require("tesseract.js");
const { text } = require("express");
const fs = require("fs");
const cors = require("cors");
const worker = createWorker();
app.use( express.json() )
app.use(cors({origin: true, credentials: true}));
app.get('/tes', (req,res) => {
res.status(200).send({
'tshirt':"ani",
})
})
app.post('/h', (req, res, err) => {
const { logo } = req.body;
if(!logo) {
res.status(418).send({ message: "We Need A Logo"})
}
async function nea() {
await worker.load();
await worker.loadLanguage('eng');
await worker.initialize('eng');
const text = await worker.recognize(logo);
var ret = text.data.text.replace('\n',' ');
await console.log(ret);
await res.send({
"data":`${ret}`
});
await worker.terminate();
}
}
);
app.listen(
PORT,
() => console.log(`open localhost`)
)
And the code to my app
/*
Code
<View style={styles.buttonContainer}>
<Camera style={styles.container} ref={cameraRef} >
</Camera>
<Button title="Scan Doc" onPress={takePic} />
<StatusBar style="auto" />
</View>
*/
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, SafeAreaView, Button, Image } from 'react-native';
import { useEffect, useRef, useState } from 'react';
import { Camera } from 'expo-camera';
import { shareAsync } from 'expo-sharing';
import * as MediaLibrary from 'expo-media-library';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import axios from 'axios';
export default function App() {
let cameraRef = useRef();
const [hasCameraPermission, setHasCameraPermission] = useState();
const [hasMediaLibraryPermission, setHasMediaLibraryPermission] = useState();
const [photo, setPhoto] = useState();
useEffect(() => {
(async () => {
const cameraPermission = await Camera.requestCameraPermissionsAsync();
const mediaLibraryPermission = await MediaLibrary.requestPermissionsAsync();
setHasCameraPermission(cameraPermission.status === "granted");
setHasMediaLibraryPermission(mediaLibraryPermission.status === "granted");
})();
}, []);
if (hasCameraPermission === undefined) {
return <Text>Requesting permissions...</Text>
} else if (!hasCameraPermission) {
return <Text>Permission for camera not granted. Please change this in settings.</Text>
}
let takePic = async () => {
let options = {
quality: 1,
base64: true,
exif: false
};
let newPhoto = await cameraRef.current.takePictureAsync(options);
setPhoto(newPhoto);
};
if (photo) {
let sharePic = () => {
shareAsync(photo.uri).then(() => {
setPhoto(undefined);
});
};
let savePhoto = (err) => {
MediaLibrary.saveToLibraryAsync(photo.uri).then(() => {
fetch('http://192.168.1.12:5000/add', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
logo: "some-data"
})
}).then(res => res.json())
.then(res => console.log(res));
}).catch(e => {
console.log(e.message)
});
};
return (
<SafeAreaView style={styles.container}>
<Image style={styles.preview} source={{ uri: "data:image/jpg;base64," + photo.base64 }} />
<Button title="Share" onPress={sharePic} />
{hasMediaLibraryPermission ? <Button title="Save" onPress={savePhoto} /> : undefined}
<Button title="Discard" onPress={() => setPhoto(undefined)} />
</SafeAreaView>
);
};
function DetailsScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
</View>
);
}
function HomeScreen({ navigation }) {
return (
<View style={styles.buttonContainer}>
<Camera style={styles.container} ref={cameraRef} >
</Camera>
<Button title="Scan Doc" onPress={takePic} />
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
<StatusBar style="auto" />
</View>
);
};
const Stack = createNativeStackNavigator();
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
width: 1000,
alignItems: 'center',
justifyContent: 'center',
},
buttonContainer: {
flex: 2,
backgroundColor: '#fff',
alignSelf: 'center'
},
preview: {
alignSelf: 'stretch',
flex: 1
},
camera: {
flex: 1,
alignItems: "center",
justifyContent: 'center'
}
});
Can somebody please help? I've spent the last 10 days trying to solve it.

how to use useState on a specific onPress Touchable Opacity

I have a screen that outputs all the groups a user isnt a member of. Each group has a join button that when clicked adds the user to the members subcollection under groups collection in firestore.
The state of the button is supposed to change from join to Joined when a user clicks the join button and then change from joined to join when the user clicks it again.
My problem is that since all the buttons have the same joinedButton state which I am listening to, changes of when a user clicks one button the state of all the buttons changes, while only the clicked one should change.
The buttons are outputted using an array map of the promise received from a firestore query.
Any ideas how I can change the state of only the button that has been clicked?
import { StyleSheet, Text, View, Image } from 'react-native'
import React, { useState, useEffect, useContext } from 'react'
import { TouchableOpacity } from 'react-native-gesture-handler'
import { db } from '../../firebase'
import { AuthContext } from '../../navigation/AuthProvider'
const DiscoverGroupList = ({ navigation }) => {
const [joinedButton, setJoinedButton] = useState(false);
const fetchGroups = async () =>{
//code to
}
const { user } = useContext(AuthContext);
const joinGroup = async (groupId) => {
try {
await db.collection('groups')
.doc(groupId)
.collection('members')
.doc(user.uid)
.set({
userId: user.uid,
isMember: true,
})
setJoinedButton(true)
} catch (error) {
console.log(error)
}
}
const leaveGroup = async (groupId) => {
try {
await db.collection('groups')
.doc(groupId)
.collection('members')
.doc(user.uid)
.delete()
setJoinedButton(false)
} catch (error) {
console.log(error)
}
}
useEffect(() => {
fetchGroups()
}, [joinedButton])
return (
<>
{groupsYouManage.map((item) => (
<View key={item.groupId} style={styles.groupWrapper}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Image source={{ uri: item.groupImage }} style={styles.groupImage} />
<View>
<Text style={styles.groupListTitle}>{item.groupName}</Text>
<Text style={styles.groupMembers}>{item.groupMembers}</Text>
</View>
</View>
{!joinedButton ? (
<TouchableOpacity style={styles.join} onPress={() => joinGroup(item.groupId)}>
<Text style={styles.joinText}>Join</Text>
</TouchableOpacity>
) : (
<TouchableOpacity style={styles.join} onPress={() => leaveGroup(item.groupId)}>
<Text style={styles.joinText}>Joined</Text>
</TouchableOpacity>
)
}
</View>
))}
</>
)
It looks like you're setting a value in the database of members collection with the ID and isMember: true. Is it possible that when you map over the data instead of rendering the button based off of the useState joinedButton, could you set the button to be rendered based on the isMember bool?
{item.isMember ? <leaveGroup button /> : <joinGroupButton />}
I think creating separate state for every item present in the array can help.
import { StyleSheet, Text, View, Image } from 'react-native'
import React, { useState, useEffect, useContext } from 'react'
import { TouchableOpacity } from 'react-native-gesture-handler'
import { db } from '../../firebase'
import { AuthContext } from '../../navigation/AuthProvider'
const DiscoverGroupList = ({ navigation }) => {
const fetchGroups = async () =>{
//code to
}
const { user } = useContext(AuthContext);
const joinGroup = async (groupId) => {
try {
await db.collection('groups')
.doc(groupId)
.collection('members')
.doc(user.uid)
.set({
userId: user.uid,
isMember: true,
})
} catch (error) {
console.log(error)
}
}
const leaveGroup = async (groupId) => {
try {
await db.collection('groups')
.doc(groupId)
.collection('members')
.doc(user.uid)
.delete()
} catch (error) {
console.log(error)
}
}
useEffect(() => {
fetchGroups()
}, [joinedButton])
return (
<>
{groupsYouManage.map((item) => {
const [joinedButton, setJoinedButton] = useState(false);
const handleJoin = () => {
joinGroup(item.groupId)
setJoinedButton(true);
}
const handleLeave = () => {
leaveGroup(item.groupId)
setJoinedButton(false);
}
return (
<View key={item.groupId} style={styles.groupWrapper}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Image source={{ uri: item.groupImage }} style={styles.groupImage} />
<View>
<Text style={styles.groupListTitle}>{item.groupName}</Text>
<Text style={styles.groupMembers}>{item.groupMembers}</Text>
</View>
</View>
{!joinedButton ? (
<TouchableOpacity style={styles.join} onPress={handleJoin }>
<Text style={styles.joinText}>Join</Text>
</TouchableOpacity>
) : (
<TouchableOpacity style={styles.join} onPress={handleLeave}>
<Text style={styles.joinText}>Joined</Text>
</TouchableOpacity>
)
}
</View>
)})}
</>
)

React Component Not Re Rendering using Spread Operator

I've tried other solutions on this site, but I am not sure why this is not working. I have a response I am getting from a server each time a button is pressed. The response comes through fine and I am able to see it each time the button is pressed. I am receiving an array of objects from the server, and I am using the useState hook to set the state of a variable to keep track of the objects in the array. However, the component does not re render. Interestingly, if I add a console.log statement to see the contents of the state variable and then save the page, I can see that the state variable was updated properly. The component still does not re render though. The relevant code is here:
import React, { useEffect, useState } from "react";
import { StyleSheet, View, TouchableOpacity, Alert } from "react-native";
import tailwind from "tailwind-rn";
import colors from "../config/colors";
import useAuth from "../hooks/useAuth";
import Screen from "../components/Screen";
import AppText from "../components/AppText";
import { MaterialCommunityIcons } from "#expo/vector-icons";
import { getData } from "../hooks/useCache";
import { useIsFocused } from "#react-navigation/native";
import ListScreen from "./ListScreen";
const ProfileScreen = () => {
const { logout, user } = useAuth();
const [likes, setLikes] = useState("");
const [completed, setCompleted] = useState("");
const [responseJson, setResponseJson] = useState(null);
const isFocused = useIsFocused();
useEffect(() => {
const likesFunc = async() => {
setLikes(await getData("likes"));
setCompleted(await getData("eventsCompleted"));
try {
const response = await fetch(
"server url here",
{
headers: { Authorization: "Bearer " + user.idToken },
}
);
const responseJson = await response.json();
setLikes(responseJson.likes);
setCompleted(responseJson.eventsCompleted);
} catch {
Alert.alert("There has been an error processing your profile");
}
}
likesFunc();
}, []);
//get voted events
//run when questionsLeft is 0 to save num of calls
useEffect(() => {
const eventFunction = async() => {
try {
const response = await fetch(
"server url here",
{
headers: { Authorization: "Bearer " + user.idToken },
}
)
const res = await response.json();
setResponseJson([...res]);
} catch (error) {
Alert.alert(
"An error has occurred loading your questions. Close the app and try again."
);
console.log(error);
}
}
eventFunction();
}, [isFocused]);
return (
<Screen style={styles.bg}>
<View
style={[
tailwind("w-full flex-row py-4 justify-center items-center top-0"),
{ justifyContent: "space-between" },
]}
>
<AppText style={{ color: colors.white, fontSize: 30, marginLeft: 5 }}>
Hello, {user.displayName.split(" ")[0]}
</AppText>
<TouchableOpacity
style={styles.logoutButton}
onPress={() => {
Alert.alert("Log Out", "Are you sure you want to log out?", [
{
text: "Yes",
style: "destructive",
onPress: logout,
},
{
text: "Cancel",
style: "cancel",
},
]);
}}
>
<MaterialCommunityIcons
name="logout-variant"
size={25}
color={colors.primary}
/>
</TouchableOpacity>
</View>
<View
style={tailwind("w-full h-1/5 justify-center items-center")}
>
<View
style={[
tailwind("w-full flex-row p-10 justify-center"),
{ justifyContent: "space-between" },
]}
>
<View style={tailwind("justify-center items-center")}>
<AppText style={{ textDecorationLine: "underline" }}>
Total Likes
</AppText>
<AppText style={{ paddingVertical: 10 }}>{likes}</AppText>
</View>
<View style={tailwind("justify-center items-center")}>
<AppText style={{ textDecorationLine: "underline" }}>
Completed
</AppText>
<AppText style={{ paddingVertical: 10 }}>{completed}</AppText>
</View>
</View>
</View>
<View
style={tailwind("w-full h-4/5 flex-1 items-center")}
>
{responseJson == null ?
<AppText style={tailwind("mt-10")}>
Select events on the "Discover" page!
</AppText>
:
<ListScreen caller={{"sender": "profile", "json": responseJson}}/>
}
</View>
</Screen>
);
};
export default ProfileScreen;
const styles = StyleSheet.create({
logoutButton: {
color: colors.white,
paddingTop: 20,
paddingRight: 10,
},
bg: {
flex: 1,
backgroundColor: colors.black,
},
});
Update: If I set the state twice in a row, it works:
setResponseJson(null);
setResponseJson([...res]);
However, this is somewhat buggy and not optimal. Leads me to think it still is a reference issue, but I am not sure why the spread operator technique does not fix this.
Three things are wrong/weird in your code. Not sure it will fix your problem but here are they :
1- Do not pass an async function to the useEffect. Instead, create an async function inside and call it :
useEffect(() => {
const myFunc = async => {...};
myFunc();
},[]);
2- You are receiving an array of objects from your api call, so why do you want to spread it ? It would lead to multiple objects inside your state, which doesn't seems right. Just pass your res in your state as is or format it as you want.
3- You are mixing async/await pattern with the .then. Either use an async func and await the result :
const myFunc = async () => {
const resPromise = await fetch(...);
const json = await resPromise.json();
...
}
or only use the .then:
const myFunc = () => {
fetch(...)
.then(data => data.json())
.then(json => ...);
...
}

React Native: Possible unhandled promise rejection (id: 0) TypeError: Network request failed

I'm getting the following error: Possible unhandled promise rejection (id:0): Network request failed.
enter image description here
I am trying to convey a review with text and a picture in firebase. In ReviewsScreen.js display and acceptance of data is implemented, in Fire.js processing and sending. I think somewhere in Fire.js the error lies but I have no ideas what the problem is
ReviewsScreen.js
import React, { Component } from 'react';
import {
StyleSheet,
View,
TouchableOpacity,
Text,
SafeAreaView,
Image,
TextInput,
SafeAreaViewBase
} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons'
import {h, w} from '../../constants'
import Fire from '../../Fire'
import ImagePicker from 'react-native-image-picker';
const options = {
title: 'Select photo',
};
export default class ReviewsScreen extends Component {
state = {
text: '',
image: null
}
pickImage = () => 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 {
// You can also display the image using data:
// const source = { uri: 'data:image/jpeg;base64,' + response.data };
this.setState({
image: response.uri
});
}
});
handleReview = () => {
Fire.shared.addReview({text: this.state.text.trim(), localUrl: this.state.image}).then(ref => {
this.setState({text: '', image: null})
this.props.navigation.goBack()
}).catch(error => {
alert(error)
})
}
render() {
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<TouchableOpacity onPress={() => this.props.navigation.goBack()}>
<Icon name='md-arrow-back' size={24} color='blue'/>
</TouchableOpacity>
<TouchableOpacity onPress={this.handleReview}>
<Text style={{fontWeight: '500'}}>Добавить</Text>
</TouchableOpacity>
</View>
<View style={styles.inputContainer}>
<Image source={require('./img/avatar.jpg')} style={styles.avatar}/>
<TextInput
autoFocus={true}
multiline={true}
numberOfLines={1}
style={{flex: 1}}
placeholder='Нам важно ваше мнение!'
onChangeText={text => this.setState({ text })}
value={this.state.text}
>
</TextInput>
</View>
<TouchableOpacity style={styles.photo}
onPress={this.pickImage}>
<Icon name='md-camera' size={32} color="#D8D9D8"></Icon>
</TouchableOpacity>
<View syle={{marginHorizontal: 32, marginTop: 32, height: 150}}>
<Image source={{uri: this.state.image}} style={{ marginTop: 32, alignSelf: 'center', width: '50%', height: '50%'}} />
</View>
</SafeAreaView>
)
}
}
Fire.js
import firebaseConfig from './config'
import firebase from 'firebase'
class Fire {
constructor() {
firebase.initializeApp(firebaseConfig);
}
addReview = async ({ text, localUri }) => {
const remoteUri = await this.uploadPhotoAsync(localUri);
return new Promise((res, rej) => {
this.firestore
.collection("reviews")
.add({
text,
uid: this.uid,
timestamp: this.timestamp,
image: remoteUri
})
.then(ref => {
res(ref);
})
.catch(error => {
rej(error)
});
});
};
uploadPhotoAsync = async uri => {
const path = `photos/${this.uid}/${Date.now()}.jpg`;
return new Promise(async (res, rej) => {
const response = await fetch(uri);
const file = await response.blob();
let upload = firebase
.storage()
.ref(path)
.put(file);
upload.on(
"state_changed",
snapshot => {},
err => {
rej(err);
},
async () => {
const url = await upload.snapshot.ref.getDownloadURL();
res(url);
}
);
});
};
get firestore() {
return firebase.firestore();
}
get uid() {
return (firebase.auth().currentUser || {}).uid;
}
get timestamp() {
return Date.now();
}
}
Fire.shared = new Fire();
export default Fire;
You wrote "localUrl" instead of "localUri":
Fire.shared.addReview({text: this.state.text.trim(), localUrl: this.state.image}).then(ref => {

Categories

Resources