React Native Image Upload as Form Data - json

Postman formdata is working perfectly, but this returns following http 500 error. what is wrong with this block.
Response {type: "default", status: 500, ok: false, statusText:
undefined, headers: Headers, …}headers: Headers {map: {…}}ok:
falsestatus: 500statusText: undefinedtype: "default"url:
"http://cupdes.com/api/v1/create-user"_bodyInit: ""_bodyText:
""proto: Object "rtghj"
import React, {Component} from 'react';
import {Platform, StyleSheet,View,Image,ScrollView,TextInput,KeyboardAvoidingView,TouchableOpacity,TouchableHighlight,AsyncStorage,} from 'react-native';
import { Container, Header, Content, Button, Text,Input, Label,Item,Form, } from 'native-base';
import Icon from 'react-native-vector-icons/FontAwesome5';
import ImagePicker from 'react-native-image-picker';
export default class SignUp extends Component {
constructor(){
super();
this.state = {
email: "",
name: "",
password: "",
photo: null ,
errors: [],
showProgress: false,
}
}
handleChoosePhoto = () => {
const options = {
noData: true,
};
ImagePicker.launchImageLibrary(options, response => {
if (response.uri) {
this.setState({ photo: response });
}
});
};
onPressSubmitButton() {
console.log("Image ",this.state.photo,this.state.email,this.state.password,this.state.name)
this.onFetchLoginRecords();
}
async onFetchLoginRecords() {
const createFormData = () => {
var data = new FormData();
data.append("image", {
name: this.state.photo.fileName,
type: this.state.photo.type,
uri:
Platform.OS === "android" ? this.state.photo.uri : this.state.photo.uri.replace("file://", "")
});
data.append('name', this.state.name);
data.append('password',this.state.password);
data.append('email', this.state.email);
console.log("aaaa",data);
return data;
};
try {
let response = await fetch(
'http://cupdes.com/api/v1/create-user',
{
method: 'POST',
headers: {
'X-AUTH-TOKEN': 'Px7zgU79PYR9ULEIrEetsb',
'Content-Type': 'multipart/form-data'
},
body:createFormData()
}
)
.then((response) => {
console.log(response,"rtghj")
this.setState({ photo: null });
if (JSON.parse(response._bodyText) === null) {
alert("Internal server error!!!");
}else{
if (JSON.parse(response._bodyText).success === "true") {
this.props.navigation.navigate('loading');
}else{
alert("Data missing!!!");
}
}
})
} catch (errors) {
alert(errors);
}
} SignupHandler=()=>{
this.props.navigation.navigate('DrewerNav')
}
SignuptologinHandler=()=>{
this.props.navigation.navigate('SigntoLogin')
}
render() {
const { photo } = this.state;
return (
<KeyboardAvoidingView
style={styles.wrapper}
>
<View style={styles.scrollWrapper}>
<ScrollView style={styles.scrollView}>
<View style={styles.LogoContainer}>
<Image source={require('../Images/avatar1.png')} style={styles.Logo} onPress={this.handleChoosePhoto} />
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center'}}>
{photo && (
<Image
source={{ uri: photo.uri,
type: "image/jpeg",
name: photo.filename }}
style={{ width: 125, height: 125,borderRadius:80}}
/>
)
}
<TouchableOpacity >
<Icon name="image" size={30} color="#222" marginTop='30' position='absolute' onPress={this.handleChoosePhoto}position='relative'/>
</TouchableOpacity>
</View>
<Text style={styles.createNew1}> CREATE ACCOUNT</Text>
</View>
<View>
<Form style={styles.inputwrapper} >
<Item >
<Icon name="user" size={25} color="white"/>
<Input style={styles.input}
placeholder='Your name'
placeholderTextColor={'white'}
name="name"
onChangeText={text => this.setState({ name: text })}
/>
</Item>
<Item >
<Icon name="mail-bulk" size={25} color="white"/>
<Input style={styles.input}
placeholder='Your e-mail'
placeholderTextColor={'white'}
name="email"
onChangeText={text => this.setState({ email: text })}
/>
</Item>
<Item >
<Icon name="lock" size={25} color="white"/>
<Input style={styles.input}
secureTextEntry
placeholder='Your password'
placeholderTextColor={'white'}
name="password"
onChangeText={text => this.setState({ password: text })}
/>
</Item >
<Item >
<Icon name="unlock" size={25} color="white"/>
<Input style={styles.input}
secureTextEntry
placeholder='Confirm password'
placeholderTextColor={'white'}
name="password"
/>
</Item>
</Form>
</View>
<View>
<TouchableOpacity >
<Button style={styles.btnLogin} onPress={this.onPressSubmitButton.bind(this)}
>
<Text style={styles.text} >Sign Up </Text>
</Button>
</TouchableOpacity>
<TouchableOpacity onPress={this.SignuptologinHandler} >
<Text style={styles.createNew}> Have an account ?LOG IN</Text>
</TouchableOpacity>
</View>
</ScrollView>
</View>
</KeyboardAvoidingView>
);
}
}

I may be very late in posting the answer but it may be helpful for others who encountered the same error. The following workflow worked for me. I used node.js as my backend.
const options = {
quality: 1.0,
maxWidth: 500,
maxHeight: 500,
storageOptions: {
skipBackup: true,
path: 'images',
cameraRoll: true,
waitUntilSaved: true,
},
};
ImagePicker.showImagePicker(options, response => {
if (response.didCancel) {
console.log('User cancelled photo picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else {
let source = response;
this.setState({profileImage: source});
}
});
}
saveToServer(){
let {profileImage} = this.state;
// initilizing form data
let formData = new FormData();
formData.append('file', {
uri: profileImage.uri,
type: 'image/jpeg/jpg',
name: profileImage.fileName,
data: profileImage.data,
});
axios.post('http://192.156.0.22:3000/api/updateProfile', userDetail, {
headers: {'Content-Type': 'multipart/form-data'},
}).then(res => //)
.catch(err => //);
}
And in the node server, I am doing something like this.
//index.js
//..
const formData = require('express-form-data');
//..
//
app.use(formData.parse());
// profile.js
profile.post('/updateProfile', async (req, res) => {
let imageCloud = await cloudinary.v2.uploader.upload(req.files.file.path, {
use_filename: true
});
}
Note: I'm using cloudinary to store my images.
The above code is working for both android and iOS.
I hope this will help you in some extent.

I don't know if you are running into an issue with just Android but I had errors posting images and videos on Android only where I was getting Network Request failed.
More info here Fetch requests failing on Android to AWS S3 endpoint
The solution was basically that the fileType sent in formData needed to be have the '/jpg' or '/mp4' in my cases and ImagePicker only returns type 'image' or 'video'. iOS apparently allows the request fine but Android will not.

its seem to be last replay you can check also this my artical.
uploadImageToServer = () => {
RNFetchBlob.fetch('POST', base.BASE_URL + '/php_imagefileupload.php', {
Authorization: "Bearer access-token",
otherHeader: "foo",
'Content-Type': 'multipart/form-data',
}, [
{ name: 'image', filename: 'image.png', type: 'image/png', data: this.state.data },
{ name: 'image_tag', data: this.state.Image_TAG, data: this.state.username }
]).then((resp) => {
var tempMSG = resp.data;
tempMSG = tempMSG.replace(/^"|"$/g, '');
Alert.alert(tempMSG);
}).catch((err) => {
// ...
})
}
blog url : https://www.banglacleverprogrammer.life/2020/05/react-native-upload-image-to-server.html

I have the same issue, it happens on Android, but works well on IOS.
I found this issue about Flipper_Network.
For now, I commented
initializeFlipper(this, getReactNativeHost().getReactInstanceManager())
in the file MainApplication.java

createFormData = () => {
console.log("RESPOSTA FORMDATA")
console.log("NAME: " + this.state.photo.fileName);
console.log("TYPE: " + this.state.photo.type);
console.log("URI: " + this.state.photo.uri);
console.log("PATH: " + this.state.photo.path);
var foto = {
uri: Platform.OS === "android" ? this.state.photo.uri : this.state.photo.uri.replace("file://", ""),
type: this.state.photo.type,
name: this.state.photo.fileName,
path: this.state.photo.path
};
const fotoUsuario = new FormData();
fotoUsuario.append("foto", foto)
return fotoUsuario;
};

Related

React Native fetch request is not displaying user's post properties

Just a simple React Native fetch request to MongoDB database. I'm trying to display dynamic JSON properties for a user's post, for example postedBy, title, etc. But when I set something like title or description I get nothing at all, just blankness.
Post.js
const mongoose = require('mongoose')
const bcrypt = require('bcrypt')
const { ObjectId } = mongoose.Schema.Types
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
postdesc: {
type: String,
required: true
},
img: {
type: String,
required: true
},
postedBy: {
type: ObjectId,
ref: "User"
}
})
mongoose.model("Post", postSchema)
fetch api code
router.get('/allposts', requireLogin, (req, res)=>{
Post.find()
.populate('postedBy', '_id username')
.then( posts =>{
res.json({ posts })
})
.catch( err=>{
console.log(err)
})
})
fetch code
const [ data, setData ] = useState([])
useEffect(()=>{
AsyncStorage.getItem('user')
.then(async (data) => {
fetch('http://10.0.2.2:3000/allposts', {
headers: {
'Authorization': 'Bearer ' + JSON.parse(data).token
}
}).then(res=>res.json())
.then( result=>{
console.log(result)
setData(result.posts)
})
})
}, [])
return code
return(
<View style={styles.container}>
<View style={styles.c1}>
{
data.map(( item )=>{
return(
<View key={item}>
<Text style={styles.postedBy} >{ data.postedBy }</Text>
<Text style={styles.title} >{ data.title }</Text>
</View>
)
})
}
</View>
</View>
)
}
What I want is: The title of my post is "Here is a title from db".
What I'm getting is: The title of my post is [Blank space] .
Note: the console.log(result) is showing the post's json in console just fine.
LOG {"posts": [{"__v": 0, "title": "Here is a title"`...etc, etc }]}

Invalid attempt to spread non-iterable instance. In order to be iterable, non-array objects must have a [Symbol.iterator]() method. in my app

i tried to show search result feature by using flatlist (json API) and search bar in my code, but i found this error and can't solve it. I can run the search but can't display the search results, can anyone help me solve this error, because this is for my college assignment
this is my code
import {
Box,
Text,
Image,
Heading,
Center,
ScrollView,
Spinner,
FlatList,
Pressable,
View,
Stack,
} from 'native-base';
import React, { Component } from 'react';
import { Dimensions } from 'react-native';
import { ListItem, SearchBar } from '#rneui/themed';
const windowWidth = Dimensions.get('window').width;
class Ilegal extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
isLoading: true,
isFetching: false,
searchTxt: null,
error: null,
temp: [],
};
};
async getData() {
try {
const response = await fetch(
'https://ojk-invest-api.vercel.app/api/illegals'
);
const json = await response.json();
this.setState({ data: json.data.illegals });
this.setResult(json);
} catch (error) {
console.error(error);
} finally {
this.setState({
isLoading: false,
isFetching: false,
});
}
};
setResult = (res) => {
this.setState ({
data: [...this.state.data, ...res],
temp: [...this.state.temp, ...res],
error: res.error || null,
isLoading: false,
});
};
renderHeader = () => {
return <SearchBar placeholder='Cari Perusahaan..'
lightTheme round editable={true}
value={this.state.searchTxt}
onChangeText={this.updateSearch}
/>
};
updateSearch = searchTxt => {
this.setState({searchTxt}, () => {
if ('' == searchTxt) {
this.setState({
data: [...this.state.temp],
});
return;
}
this.state.data = this.state.temp.filter(function (item) {
return item.name.includes(searchTxt);
}).map(function({ id, name, type }) {
return {id, name, type};
});
});
};
componentDidMount() {
this.getData();
};
onRefresh = () => {
this.setState({ isFetching: true }, () => {
this.getData();
});
};
renderItem = ({ item }) => {
const { navigation } = this.props;
return (
<>
<Pressable
style={{ padding: 20, backgroundColor: 'white' }}
onPress={() => navigation.navigate('Detail Ilegal', { data: item })}>
<Center>
<View
style={{
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}}>
<Text
style={{
fontSize: 16,
width: windowWidth - 40,
textAlign: 'justify',
marginTop: 6,
}}>
{item.name}
</Text>
<Text
style={{
color: '#808080',
fontSize: 14,
width: windowWidth - 40,
textAlign: 'justify',
marginTop: 6,
}}>
{item.type}
</Text>
</View>
</Center>
</Pressable>
<View style={{ borderWidth: 0.5, borderColor: '#cccccc' }}></View>
</>
);
};
render() {
const { data, isLoading, isFetching, error} = this.state;
return (
<View style={{ flex:1, justifyContent:'center', backgroundColor:'white', flexDirection: 'column', }}>
{isLoading ? (
<Spinner size="large" color="#AA0002" />
) : (
<FlatList
ListHeaderComponent={this.renderHeader}
data={data}
keyExtractor={({ link }, index) => link}
renderItem={this.renderItem}
onRefresh={this.onRefresh}
refreshing={isFetching}
/>
)}
</View>
);
}
}
export default Ilegal;
you can help me for fixed this error ?
I think the error is in setResult() function as far as I could understood your set result function. You are setting data like this
this.setState({ data: json.data.illegals });
in getData() while you are fetching data from API but you are sending json as parameter to setResult which is object. Try to change your setResult() as below:
setResult = (res) => {
this.setState ({
data: [...this.state.data, ...res.data.illegals],
temp: [...this.state.temp, ...res.data.illegals],
error: res.error || null,
isLoading: false,
});
};

Objects are not valid as a React child (found: object with keys { my keys })

when I submit my form with the const onSubmit, I fetch data. If the submit form is ok, I want to extract the data I get from my api. So I use, setState reponse : my data, and then I want to pass this data (reponse) to the component SingleLayout.
But it doesn't work. I get this error : [Error: Objects are not valid as a React child (found: object with keys {ID, Post Title, post_date, Prix, Surface, Ville, Category, featured_image, mandats_signes, date_de_signature_du_compromis}). If you meant to render a collection of children, use an array instead.]
Here the code :
export default class QueryChild extends Component {
state = {
username: "",
password: "",
isLogged: null,
email: "",
reponse: '',
id: 4577
}
constructor(props) {
super(props);
this.render();
}
onSubmit = async() => {
const fetchValue = 'myAPI/suivi-dossier?' + 'username=' + this.state.username + '&password=' + this.state.password + '&id=' + this.state.id
try {
await fetch(fetchValue, {
method: 'GET'
})
.then((response) => response.json())
.then((responseJson) => {
if(responseJson.success === true) {
// I want to extract this :
this.setState({ reponse: responseJson.message })
this.setState({ isLogged: true })
} else {
this.setState({reponse : responseJson.message });
console.log(this.state.reponse)
}
// this.setState({
// data: responseJson
// })
})
.catch((error) => {
console.error(error);
});
} catch (err) {
console.log(err)
}
}
render() {
if(this.state.isLogged === true) {
// when user is Logged, I want to pass this.state.reponse to my component SingleLayout
// but I get the error : [Error: Objects are not valid as a React child
// (found: object with keys {ID, Post Title, post_date, Prix, Surface, Ville,
// Category, featured_image, mandats_signes, date_de_signature_du_compromis}). If you
// meant to render a collection of children, use an array instead.]
const SingleComponent = this.state.reponse.map(message => <SingleLayout key={message.ID} table={message}/> )
return (
<SafeAreaView>
{SingleComponent}
</SafeAreaView>
);
} else {
return (
<View style={styles.container}>
<ScrollView>
<ImageBackground source={image} resizeMode="cover" style={styles.image}>
<Spacer taille={70} />
<View style={styles.content}>
<Image
style={styles.imgLogo}
source={require('../../logo.png')}
/>
<Text style={styles.logo}>Pour des raisons de sécurité, veuillez entrer vos Identifiants</Text>
<View style={styles.inputContainer}>
<Text style={styles.textBasique}>{this.state.reponse}</Text>
<TextInput
placeholder='Identifiant ou adresse email'
style={styles.input}
value={this.state.username}
onChangeText={val => this.setState({ username: val})}
/>
<TextInput
placeholder='Mot de passe'
style={styles.input}
value={this.state.password}
secureTextEntry= {true}
onChangeText={val => this.setState({ password: val})}
/>
<Spacer taille={10} />
<Button title='Se Connecter' onPress={this.onSubmit}/>
<Spacer taille={10} />
</View>
</View>
<Spacer taille={200} />
</ImageBackground>
</ScrollView>
</View>
)
}
}
}
Thank you.
The response that comes from the server is an object. Since the "map" function only works with an array so, if you want to run this code you've to wrap "responseJson.message" into the array.
if(responseJson.success === true) {
this.setState({ reponse: [responseJson.message] })
this.setState({ isLogged: true })
}
Yes I get similar error after applying the solution because I found that the error comes from the setState reponse. So I added JSON.stringify and now the error is gone.
The code of Single Layout :
const SingleLayout = (props) => {
let id = props.id
let table = props.table
console.log(table)
const { colors } = useTheme();
return (
<View style={[styles.container, {backgroundColor: colors.background}]}>
<Text>Bravo</Text>
</View>
)
}
Now with the solution : const SingleComponent = Object.keys(this.state.reponse).map(key => )
it works but it send caracters one by one
SO I finally solved my problem :
the code i change :
QueryChild.tsx :
if(responseJson.success === true) {
//here :
this.setState({ reponse: JSON.stringify(responseJson.message) })
this.setState({ isLogged: true })
and I directly return SingleLayout like that :
return (
<SafeAreaView>
<SingleLayout table={this.state.reponse} />
</SafeAreaView>
Now I can access this.state.reponse into my SingleLayout :
const SingleLayout = (props) => {
let id = props.id
let table = props.table
const myObj = JSON.parse(table);
console.log(myObj[0].mandats_signes)
console.log(myObj[0].ID)
console.log(myObj[0].Category)
//etc...
const { colors } = useTheme();
return (
<View style={[styles.container, {backgroundColor: colors.background}]}>
<Text>Bravo</Text>
</View>
)
}
it's not a good practice to pass JSON and then parse.
I solved it and here is the link with the same solution that I presented to you:
https://codesandbox.io/s/peaceful-bouman-qyi5y?file=/src/App.js

React Native fetch API response not displaying

I am creating an app using expo. You can check the snack here
I am also giving the code here:
import React, {Component} from 'react';
import { ActivityIndicator, Text, View, StyleSheet, FlatList, Alert, TouchableOpacity } from 'react-native';
import {Avatar, Card, Button, Divider, ListItem, Image} from 'react-native-elements';
import Icon from 'react-native-vector-icons/FontAwesome';
import Constants from 'expo-constants';
import HTML from 'react-native-render-html';
import UserAvatar from 'react-native-user-avatar';
import { StackNavigator } from 'react-navigation';
import { createAppContainer} from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
class HomeScreen extends React.Component{
static navigationOptions =
{
title: '',
};
constructor(props){
super(props);
this.state = {
Loading : true,
data : []
}
}
fetchLeash(){
fetch('https://lishup.com/app/')
.then((response) => response.json())
.then((responseJson) => {
this.setState({ data: responseJson, Loading:false });
}).catch((error) => {
Alert.alert('error!');
});
}
fetchImage(getimg){
fetch('https://lishup.com/app/fetch-image.php', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
image: getimg
})
}).then((response) => response.json())
.then((responseJson) => {
return (<Text>responseJson.url</Text>);
}).catch((error) => {
Alert.alert('error');
});
}
componentDidMount(){
this.fetchLeash();
}
renderLeash = ({ item }) => (
<View>
<Card style={{ height:100, justifyContent: 'center', alignItems: 'center' }}>
<ListItem
leftAvatar={{
title: item.user,
source: { uri: item.userpic },
}}
title={item.user}
subtitle={item.time}
chevron
/>
<Divider style={{margin:5, backgroundColor:'white'}} />
<HTML html={item.text} />
{this.fetchImage(item.images)}
</Card>
</View>
)
render(){
if(this.state.Loading == true){
return(
<ActivityIndicator size="large" style={{marginTop:100}} color="#0000ff" />
);
}else{
return(
<View>
<FlatList style={{width:400}}
data={this.state.data}
renderItem={this.renderLeash} />
</View>
);
}
}
}
const styles = StyleSheet.create({
});
const RootStack = createStackNavigator(
{
Home: { screen: HomeScreen },
},
{
initialRouteName: 'Home',
}
);
export default createAppContainer(RootStack);
If you run the snack in your device, you will see that the posts(fetchLeash() function) is working fine. But the fetchImage() is returning nothing.
My fetch-image.php file is here:
<?php
// Importing DBConfig.php file.
include 'DB.php';
header('Content-Type: application/json');
// Creating connection.
$con = mysqli_connect($HostName,$HostUser,$HostPass,$DatabaseName);
// Getting the received JSON into $json variable.
$json = file_get_contents('php://input');
// decoding the received JSON and store into $obj variable.
$obj = json_decode($json,true);
// Populate User email from JSON $obj array and store into $email.
$image = $obj['image'];
if($image == "") {
$blank[] = array("url"=>"");
echo json_encode($blank);
}else{
//query to get image url with the code received
$Sql_Query = "SELECT * FROM `leash_img` WHERE `pid`= '".$image."' ";
// Executing SQL Query.
$check = mysqli_query($con,$Sql_Query);
if($check){
while($row=mysqli_fetch_assoc($check)){
$SuccessLoginMsg[] = array("url"=> $row['image']);
}
// Converting the message into JSON format.
$SuccessLoginJson = json_encode($SuccessLoginMsg);
echo $SuccessLoginJson;
}
}
?>
This returns like the following:
[{"url":"link here"}]
The PHP file is working fine. But the react native fetchImage() is not working.
I am totally new to react native. So forgive my problems. I am just out of my ideas. Please help me.
You can't asynchronously render UI from the render function, you need to fetch the data outside it in one of the lifecycle functions and conditionally render UI while it is being fetched.
Once the data has been fetched you should go ahead and fetch the image urls. Use Promise.all and map each response item to a fetch request. This will allow all image url fetches to resolve asynchronously and maintain index order.
fetchLeash() {
fetch('https://lishup.com/app/')
.then((response) => response.json())
.then((responseJson) => {
this.setState({ data: responseJson });
Promise.all(responseJson.map(({ images }) => this.fetchImage(images)))
.then((images) => {
this.setState({ imageUrls: images.map(url => ({ uri: url })) })
});
})
.catch((error) => {
Alert.alert('error!');
})
.finally(() => {
this.setState({ Loading: false });
});
}
The other important change is that the image response is an array of length 1, so need to access correctly.
fetchImage(image) {
return fetch('https://lishup.com/app/fetch-image.php', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ image }),
})
.then((response) => response.json())
.then((responseJson) => responseJson[0].url);
}
Now you can conditionally render an Image if the url at that index exists.
renderLeash = ({ item, index }) => (
<View>
<Card
style={{ height: 100, justifyContent: 'center', alignItems: 'center' }}>
<ListItem
leftAvatar={{
title: item.user,
source: { uri: item.userpic },
}}
title={item.user}
subtitle={item.time}
chevron
/>
<Divider style={{ margin: 5, backgroundColor: 'white' }} />
<HTML html={item.text} />
<Text>
{this.state.imageUrls[index] && this.state.imageUrls[index].uri}
</Text>
{this.state.imageUrls[index] && (
<Image
source={this.state.imageUrls[index]}
style={{ width: 100, height: 100 }}
PlaceholderContent={<ActivityIndicator />}
/>
)}
</Card>
</View>
);
Expo Snack
EDIT Allow display of all fetched image URLs. Instead of grabbing and returning just the first URL, return an array of URLs. Below I mapped the URLs to a new array before returning them, and these can be set directly in state now. Update the render function to use an additional guard (array length check) and render null if array doesn't exist. (Could also use another FlatList here if you wanted to)
fetchLeash() {
return fetch('https://lishup.com/app/')
.then((response) => response.json())
.then((responseJson) => {
this.setState({ data: responseJson });
Promise.all(
responseJson.map(({ images }) => this.fetchImage(images))
).then((imageUrls) => this.setState({ imageUrls }));
})
.catch((error) => {
Alert.alert('error!');
})
.finally(() => {
this.setState({ Loading: false });
});
}
fetchImage(image) {
return fetch('https://lishup.com/app/fetch-image.php', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ image }),
})
.then((response) => response.json())
.then((responseJson) =>
// Filter elements with empty string URLs, then app just the URL
responseJson.filter(({ url }) => url).map(({ url }) => url)
);
}
...
{this.state.imageUrls[index] && this.state.imageUrls[index].length
? this.state.imageUrls[index].map((uri) => (
<Image
source={{ uri }}
style={{ width: 100, height: 100 }}
PlaceholderContent={<ActivityIndicator />}
/>
))
: null}

Trying to put API call and getting "Actions must be plain objects. Use custom middleware for async actions."

I am trying to make a login and logout pages using my API and Redux.
Currently I am having types: LOGIN, LOGIN_FAILED and LOGOUT.
In reducer I created a case for every type
This is how it looks like:
import React, { Component } from "react";
import {
LOGIN,
LOGIN_FAILED,
LOGOUT
} from '../types'
const defaultState = {
isLoggedIn: false,
UserName: '',
UserEmail: '',
UserPassword: ''
};
export default function reducer(state = defaultState, action) {
switch (action.type) {
case LOGIN:
return Object.assign({}, state, {
isLoggedIn: true,
UserName: action.UserName,
UserEmail: action.UserEmail,
UserPassword: action.UserPassword
});
case LOGOUT:
return Object.assign({}, state, {
isLoggedIn: false,
UserName: '',
UserEmail: '',
UserPassword: ''
});
case LOGIN_FAILED:
return {
UserName: '',
UserEmail: '',
UserPassword: '',
isLoggedIn: false
}
default:
return state;
}
And these are actions:
import {
LOGIN,
LOGIN_FAILED,
LOGOUT
} from '../types'
export const login = (UserName, UserEmail, UserPassword) => async (dispatch) => {
function onSuccess(success) {
dispatch({ type: LOGIN, payload: success })
return success
}
function onError(error) {
dispatch({ type: LOGIN_FAILED, error })
return error
}
try {
const { UserEmail } = this.state ;
const { UserName } = this.state ;
const { UserPassword } = this.state ;
const res = await fetch('https://lifestormweb.000webhostapp.com/User_Login.php', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: UserEmail,
password: UserPassword,
name: UserName
})
}).then((response) => response.json())
.then((responseJson) => {
// If server response message same as Data Matched
if(responseJson === 'Data Matched')
{
//Then open Profile activity and send user email to profile activity.
this.props.navigation.navigate("Profil");
}
else{
Alert.alert(responseJson);
}
})
const success = await res.json()
return onSuccess(success)
} catch (error) {
return onError(error)
}
};
export const logout = () => {
return {
type: 'LOGOUT'
};
};
Login.js page:
export class Login extends Component {
state = {
UserName: '',
UserEmail: '',
UserPassword: ''
}
userLogin (e) {
this.props.onLogin(this.state.UserName, this.state.UserEmail, this.state.UserPassword);
e.preventDefault();
}
render() {
return (
<View style={styles.container}>
<View style={styles.loginTextCont}>
<Text style={{fontSize: 36, fontFamily: "Futura" }}>
Willkommen zu</Text> <Text style={{fontSize: 36, fontFamily: "Futura", color:'#ff0000' }}>LifeStorm!</Text>
<View style={{width: 10, height: 5 }} />
</View>
<TextInput style={styles.inputBox}
autoCapitalize='none'
autoCorrect={false}
autoFocus={true}
keyboardType='email-address'
placeholder="Ihre Name"
placeholderTextColor = "#ffffff"
selectionColor="#ffffff"
value={this.state.UserName}
onChangeText={(text) => this.setState({ UserName: text })} />
<TextInput style={styles.inputBox}
autoCapitalize='none'
autoCorrect={false}
autoFocus={true}
keyboardType='email-address'
placeholder="Ihre E-Mail"
placeholderTextColor = "#ffffff"
selectionColor="#ffffff"
value={this.state.UserEmail}
onChangeText={(text) => this.setState({ UserEmail: text })} />
<TextInput style={styles.inputBox}
placeholder='Password'
autoCapitalize='none'
autoCorrect={false}
placeholder="Ihre Passwort"
placeholderTextColor = "#ffffff"
selectionColor="#ffffff"
secureTextEntry={true}
value={this.state.UserPassword}
onChangeText={(text) => this.setState({ UserPassword: text })} />
<TouchableOpacity
style={styles.button}
onPress={(e) => this.userLogin(e)}
>
<Text style={styles.buttonText}>Sich einloggen</Text>
</TouchableOpacity>
<View style={styles.signupTextCont}>
<Text style={styles.signupText}>
Haben Sie kein Konto?
</Text>
<TouchableOpacity onPress={()=>this.props.navigation.navigate("Register")}> <Text style={styles.signupButton}> Sich anmelden</Text></TouchableOpacity>
</View>
</View>
);
}
}
const mapStateToProps = (state, ownProps) => {
return {
isLoggedIn: state.auth.isLoggedIn,
};
}
const mapDispatchToProps = (dispatch) => {
return {
onLogin: (UserName, UserEmail, UserPassword) => { dispatch(login(UserName, UserEmail, UserPassword)); }
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
Now every time when I try to log in I receive following error:
Actions must be plain objects. Use custom middleware for async actions
What am I missing?
I think your problem is returning your success/error objects instead of just dispatching. Can you try this and see if the problem goes:
function onSuccess(success) {
return dispatch({ type: LOGIN, payload: success })
// return success -- delete this
}
function onError(error) {
return dispatch({ type: LOGIN_FAILED, error })
// return error -- delete this
}