React Redux Saga API call - api

I am building a food delivery app using react-native in which I require to display restaurant list from an API call. I decided to implement it using Redux-saga a while back.
After searching through tutorials and different examples on Github I couldn't grasp the concept. Hence my question how do I make sure to dispatch an action while navigating from login screen to home screen which displays the restaurant as desired in Home screen from API response or more specifically store API response in saga index and access it in Home Screen. Sorry for messy code(still a beginner). I apologise in advance for my mistakes in question(my first time asking here).
src/Sagas/index.js
import { put, delay, call } from "redux-saga/effects";
import { takeEvery, takeLatest } from "redux-saga";
import axios from "axios";
export function* incrementAsync() {
yield delay(1000);
yield put({ type: "INCREMENT" });
}
export function* fetchRestaurantAsync() {
try {
console.log("Calling API");
const response = yield call(
axios.get,
"http://18.188.213.236/fooddelivery/api_v2/Homemaster/getCategories/0/1/25.204849/55.27078"
);
console.log("reponse", response);
yield put({ type: "FETCH_SUCCEEDED" }, restaurant);
const restaurant = response ? response.data.category : [];
// const data = yield call(Api.fetchUser, action.payload.url);
// yield put({ type: "FETCH_SUCCEEDED", data });
} catch (error) {
console.log("requestfailed: could not ");
console.log("error", error);
// yield put({ type: "FETCH_FAILED", error });
}
}
export function* watchfetchRestaurant() {
yield takeEvery("FETCH_REQUESTED", fetchRestaurantAsync);
}
export function* watchCart() {
yield takeEvery("INCREMENT_ASYNC", incrementAsync);
}
export default function* rootSaga() {
yield { watchCart };
yield { watchfetchRestaurant };
}
src/components/Home.js
import React, { Component } from "react";
import { Text, View, ScrollView, Image, StyleSheet } from "react-native";
import { Container, Card, CardItem, Body, Left, Right } from "native-base";
import RestaurantDetail from "./RestaurantDetail";
import axios from "axios";
import { connect } from "react-redux";
import LinearGradient from "react-native-linear-gradient";
import { SafeAreaView } from "react-navigation";
class Home extends Component {
constructor(props) {
super(props);
this.state = {
banner: [],
nearByRestaurant: []
};
axios
.get(
"http://18.188.213.236/fooddelivery/api_v2/Bannermaster/getBannerList/{latitude}/{longitude}"
)
.then(response => {
this.setState({ banner: response.data.result });
})
.catch(function(error) {
console.log(
"There has been a problem with your fetch operation: " + error
);
throw error;
});
axios
.get(
"http://18.188.213.236/fooddelivery/api_v2/Restaurantmaster/get_restaurants/0/25.204849/55.27078"
)
.then(response => {
console.log(response);
this.setState({ nearByRestaurant: response.data.result });
})
.catch(function(error) {
console.log(
"There has been a problem with your fetch operation: " + error
);
throw error;
});
}
renderRestaurants() {
return this.props.restaurant.map(restaurants => {
return restaurants.dishes.map(dishes => (
<RestaurantDetail key={dishes.dish_id} dishes={dishes} />
));
});
}
renderBanner() {
return (
<ScrollView
contentContainerStyle={{ flexDirection: "row" }}
horizontal={true}
>
{this.state.banner.map(result => {
return (
<View
key={result.id}
style={{
justifyContent: "center",
alignItems: "center"
}}
>
<Image
resizeMode="cover"
source={{
uri: result.image
}}
style={{ position: "relative", height: 300, width: 400 }}
/>
<Text style={styles.bannertextStyle}>{result.messages}</Text>
</View>
);
})}
</ScrollView>
);
}
rendernearByRestaurant() {
return (
<View>
<ScrollView
contentContainerStyle={{ flexDirection: "row" }}
horizontal={true}
showsHorizontalScrollIndicator={false}
>
{this.state.nearByRestaurant.map(result => {
return (
<Card
key={result.restaurant_id}
style={{
height: 200,
width: 230,
backgroundColor: "transparent"
}}
>
<Body>
<Image
resizeMode="cover"
source={{
uri: result.image
}}
style={{
height: 150,
width: 220,
borderRadius: 5
}}
/>
</Body>
<CardItem
footer
style={{ height: 50, backgroundColor: "transparent" }}
>
<Left>
<Text numberOfLines={1}>{result.restaurant_name}</Text>
</Left>
<Right>
<Text numberOfLines={1} style={{ textAlign: "center" }}>
{result.start_price +
"-" +
result.end_price +
" " +
"USD"}
</Text>
</Right>
</CardItem>
</Card>
);
})}
</ScrollView>
</View>
);
}
render() {
console.log(this.props);
return (
<SafeAreaView>
<View styles={styles.containerStyle}>
<ScrollView>
<ScrollView horizontal={true}>
<View styles={styles.bannerStyle}>{this.renderBanner()}</View>
</ScrollView>
<View>
<LinearGradient
style={{
height: 100,
borderColor: "transparent"
}}
locations={[0.4, 0.8]}
colors={["#FF8500", "#FB3D2D"]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
>
<View
style={{
flexDirection: "row",
alignContent: "flex-end",
justifyContent: "space-between"
}}
>
<Text
style={{
fontFamily: "Poppins",
fontWeight: "600",
color: "white",
fontSize: 20
}}
>
Rigel Picks
</Text>
<Image source={require("../assets/ios/filter.png")} />
</View>
</LinearGradient>
</View>
<View>
<View style={{ padding: 10 }}>
//Saga Api response as 'restaurant' here
<RestaurantDetail
restaurants={this.props.restaurant.slice(0, 1)}
navigations={this.props.navigation}
/>
</View>
<View style={{ flex: 1, padding: 10 }}>
<LinearGradient
style={{
borderColor: "transparent",
borderRadius: 8,
flex: 1
}}
locations={[0.4, 0.8]}
colors={["#FF8500", "#FB3D2D"]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
>
<CardItem header style={{ backgroundColor: "transparent" }}>
<Left>
<Text
style={{
textAlign: "left",
fontSize: 18,
color: "white",
fontWeight: "bold"
}}
>
Near By Restaurants
</Text>
</Left>
<Right>
<Text style={styles.linkStyle}>See all</Text>
</Right>
</CardItem>
<CardItem style={{ backgroundColor: "transparent" }}>
<View>{this.rendernearByRestaurant()}</View>
</CardItem>
</LinearGradient>
</View>
<View style={{ borderRadius: 8, padding: 10 }}>
//Saga Api response as 'restaurant' here
<RestaurantDetail
restaurants={this.props.restaurant.slice(1)}
navigations={this.props.navigation}
/>
</View>
</View>
</ScrollView>
</View>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
containerStyle: { flex: 1, backgroundColor: "transparent" },
bannerStyle: {
flex: 2,
alignContent: "center",
fontSize: 20,
fontWeight: "bold"
},
bannertextStyle: {
fontFamily: "Poppins",
position: "absolute",
top: 40,
fontSize: 30,
color: "white",
textTransform: "uppercase",
fontWeight: "bold"
},
gradientStyle: {
height: 30,
fontSize: 5,
fontWeight: "bold"
},
categoryRestaurantStyle: {
borderRadius: 3,
justifyContent: "space-between",
alignContent: "center"
},
imageStyle: {
height: 100,
width: 100
},
restaurantNameStyle: {
fontSize: 15,
fontWeight: "bold"
},
dishnameStyle: {
fontSize: 5,
fontWeight: "bold"
},
fontcolorStyle: {
color: "white"
},
linkStyle: {
fontFamily: "Poppins",
fontSize: 18,
color: "white",
textDecorationLine: "underline"
}
});
function mapStateToPros(state) {
return {
restaurant: state.restaurant
};
}
const HomeComponent = connect(
mapStateToPros,
null
)(Home);
export { HomeComponent as Home };
src/components/Reducers/index.js
import { combineReducers } from "redux";
import counterReducer from "./counterReducer";
import fetchReducer from "./fetchReducer";
export default combineReducers({
ctr: counterReducer,
fetch: fetchReducer
});
src/components/Reducers/fetchReducer.js
export default (state = { restaurant: [] }, action) => {
switch (action.type) {
case "FETCH_REQUESTED":
case "FETCH_SUCCEEDED":
return {
...state,
restaurant: action.payload
};
case "FETCH_FAILED":
return {
...state,
restaurant: []
};
default:
return state;
}
};

I found the mistake. It was in Saga/index.js. I was not calling sagas properly in rootSaga(). I changed it to yield all([call (saga1),call(saga2)...,call(saga11)]).

Related

Why is the checkbox not setting checked and why does console.log is called multiple times?

Here is my Login screen
import {
View,
StyleSheet,
Text,
TouchableWithoutFeedback,
ScrollView,
Platform,
KeyboardAvoidingView,
Alert,
Image,
TouchableOpacity,
} from "react-native";
import * as LocalAuthentication from "expo-local-authentication";
import { CustomCheckBoxInput, CustomTextInput } from "#components/Input";
import { useForm } from "react-hook-form";
import { Ionicons } from "#expo/vector-icons";
import colors from "#config/colors";
import { AppButton } from "#components/Button";
import AppScreen from "#components/AppScreen";
import { loginUser } from "#redux/slices/auth";
import { useCallback, useEffect, useState, memo, useMemo } from "react";
import { useAppDispatch, useAppSelector } from "#hooks/hooks";
import useAuth from "#hooks/useAuth";
import { getItem, removeItem, setItem } from "#config/dataStorage";
import { useFocusEffect, useIsFocused } from "#react-navigation/native";
interface LoginScreenProps {}
const LoginScreen = ({ navigation }: any) => {
const [data, setData] = useState(null);
const { loading, logIn } = useAuth();
const { control, handleSubmit, reset } = useForm();
const focused = useIsFocused();
const getData = async () => {
const data = await getItem("#user_data");
const parsedData = JSON.parse(data);
if (parsedData) setData(parsedData);
};
useEffect(() => {
if (focused) {
getData();
const defaults = {
email: data?.email,
remember: data?.isRemembered,
};
reset(defaults);
}
}, [reset, data?.email, focused]);
console.log(data);
const onSubmit = useCallback(async (data) => {
const response = await logIn(data);
if (data.remember && response.success) {
await setItem(
"#user_data",
JSON.stringify({ email: data.email, isRemembered: data.remember })
);
} else {
await removeItem("#user_data");
}
}, []);
const onFaceId = async () => {
try {
// Checking if device is compatible
const isCompatible = await LocalAuthentication.hasHardwareAsync();
if (!isCompatible) {
throw new Error("Your device isn't compatible.");
}
// Checking if device has biometrics records
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
if (!isEnrolled) {
throw new Error("No Faces / Fingers found.");
}
// Authenticate user
await LocalAuthentication.authenticateAsync();
Alert.alert("Authenticated", "Welcome back !");
} catch (error) {
Alert.alert("An error as occured", error?.message);
}
};
return (
<AppScreen>
<KeyboardAvoidingView
style={{
flex: 1,
paddingHorizontal: 20,
}}
behavior={Platform.OS === "ios" ? "padding" : "height"}
>
<Image
source={require("assets/logo5.png")}
resizeMode="contain"
style={{ position: "absolute", top: 0, left: 0 }}
/>
<ScrollView>
<View
style={{
flexDirection: "column",
marginTop: "10%",
marginBottom: "3%",
}}
>
<View
style={{
flexDirection: "row",
alignItems: "center",
}}
>
<TouchableWithoutFeedback
onPress={() => navigation.navigate("Welcome")}
>
<Ionicons
name="chevron-back-outline"
size={30}
color="#fff"
style={{ marginStart: -10 }}
/>
</TouchableWithoutFeedback>
<Text style={{ color: "white", fontSize: 32 }}>
Welcome back,
</Text>
</View>
</View>
<Text
style={{
color: "white",
fontSize: 18,
fontWeight: "200",
marginBottom: "5%",
}}
>
Please login to your account to continue.
</Text>
<Text style={{ color: "#fff" }}>{data?.email}</Text>
<Text style={{ color: "#fff" }}>{data?.isRemembered.toString()}</Text>
<CustomTextInput
name="email"
control={control}
placeholder="email"
value={data?.email}
textContentType="emailAddress"
label="E-mail"
rules={{ required: "This field is required!" }}
/>
<CustomTextInput
name="password"
control={control}
placeholder="password"
label="Password"
rules={{ required: "This field is required!" }}
secureTextEntry={true}
/>
<CustomCheckBoxInput
control={control}
name="remember"
label="Remember Me"
isChecked={data?.isRemembered}
/>
<AppButton
title="Login"
bgColor={colors.buttonPrimary}
loading={loading}
onPress={handleSubmit(onSubmit)}
style={{ marginTop: 10, marginBottom: 10 }}
/>
<View
style={{ flexDirection: "row", justifyContent: "space-between" }}
>
<TouchableOpacity
activeOpacity={0.7}
style={{ marginBottom: 20, alignSelf: "flex-end" }}
onPress={onFaceId}
>
<Text
style={{
color: colors.buttonPrimary,
fontWeight: "300",
fontSize: 16,
}}
>
Login with Face ID
</Text>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.7}
style={{ marginBottom: 20, alignSelf: "flex-end" }}
onPress={() => navigation.navigate("Email")}
>
<Text
style={{
color: colors.smallTextHex,
fontWeight: "600",
fontSize: 16,
}}
>
Forgot Password
</Text>
</TouchableOpacity>
</View>
<View
style={{
flexDirection: "row",
justifyContent: "center",
}}
>
<Text style={{ color: "#fff", fontSize: 16, fontWeight: "200" }}>
New to TickersFund?{" "}
</Text>
<TouchableOpacity
onPress={() => navigation.navigate("Register")}
activeOpacity={0.7}
>
<Text
style={{
color: colors.smallTextHex,
fontWeight: "600",
color: colors.buttonPrimary,
fontSize: 16,
}}
>
Register Here
</Text>
</TouchableOpacity>
</View>
</ScrollView>
</KeyboardAvoidingView>
</AppScreen>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: colors.background,
// padding: 10,
},
top_container: {},
});
export default LoginScreen;
What I wanted is showing defaultValue for Email and Checkbox if user submits data with remember true
Problem
Email showing but checkbox is not showing checked
multiple console.log() showing
react-native-async-storage: ~1.15
Why am I seeing multiple console.logs and why value is not setting in the CustomCheckBox
export const CustomCheckBoxInput = ({
control,
name,
label,
...props
}: CustomChecboxProps) => {
return (
<Controller
control={control}
name={name}
render={({
field: { value, onChange, onBlur },
fieldState: { error },
}) => (
<BouncyCheckbox
onPress={(value) => {
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
onChange(value);
}}
fillColor={colors.buttonPrimary}
size={18}
unfillColor={colors.background}
style={{ marginTop: 10, marginLeft: 5 }}
text={label}
textStyle={{ fontSize: 18, textDecorationLine: "none" }}
iconStyle={{ borderColor: colors.buttonPrimary, marginRight: -5 }}
{...props}
/>
)}
/>
);
};
Here is my CustomCheckBox component

Upload an image to Firebase with React Native

i am trying to make a photo uploader to firebase in react native. I followed a tutorial and adopted the code 1-on-1. However, after I want to execute the code everything appears to work until the code has to be uploaded then I get the following error:
Possible Unhandled Promise Rejection (id: 0): Error: An unknown error
has occurred.
fn#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:2132:45
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:127402:44
putFile#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:137147:104
uploadImage#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:210966:91
touchableHandlePress#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:54240:47
touchableHandlePress#[native code]
_performSideEffectsForTransition#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:52872:36
_performSideEffectsForTransition#[native code]
_receiveSignal#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:52798:46
_receiveSignal#[native code] touchableHandleResponderRelease#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:52677:26
touchableHandleResponderRelease#[native code]
invokeGuardedCallbackImpl#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:8997:21
invokeGuardedCallback#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9093:42
invokeGuardedCallbackAndCatchFirstError#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9097:36
executeDispatch#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9204:48
executeDispatchesInOrder#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9224:26
executeDispatchesAndRelease#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9329:35
forEach#[native code]
forEachAccumulated#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9319:22
runEventsInBatch#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9353:27
runExtractedPluginEventsInBatch#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9441:25
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:10467:42
batchedUpdates$1#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:21921:20
batchedUpdates#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:10415:36
_receiveRootNodeIDEvent#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:10466:23
receiveTouches#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:10496:34
__callFunction#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:2650:49
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:2363:31
__guard#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:2604:15
callFunctionReturnFlushedQueue#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:2362:21
callFunctionReturnFlushedQueue#[native code]
Is anyone familiar with uploading images to firebase? in the tutorial they use uuid but for some reason the app breaks when I try to use this, so I left it out. this is the corresponding code:
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, Image, Button, ScrollView, ImageBackground, Dimensions,TouchableOpacity, FlatList,
AsyncStorage} from 'react-native';
import { Avatar, ListItem } from 'react-native-elements';
import Icon from 'react-native-vector-icons/FontAwesome';
import ImagePicker from 'react-native-image-picker';
import firebase from 'react-native-firebase';
const SCREEN_WIDTH = Dimensions.get("window").width;
const list = [
{
title: '',
icon: 'cake',
url: 'ProfileSettings',
},
]
const options = {
title: 'Select Image',
storageOptions: {
skipBackup: true,
path: 'images'
}
};
const ImageRow = ({ image, windowWidth, popImage }) => (
<View>
<Image
source={{ uri: image }}
style={[styles.img, { width: windowWidth / 2 - 15 }]}
onError={popImage}
/>
</View>
);
class profileScreen extends Component {
constructor(props){
super(props);
this.state = {
first: '',
place: '',
province: '',
uid: '',
profileImg: '',
email: '',
imgSource: '',
uploading: false,
progress: 0,
images: []
}
}
getUserData = (user) => {
console.log(user);
let ref = firebase.database().ref('Users/' + user);
ref.on('value' , snapshot =>{
var state = snapshot.val();
this.setState({
first: state.firstname,
place: state.place,
province: state.province,
uid: user,
profileImg: state.profileImg,
birthday: state.birthday,
email: state.email,
year: state.registered.split("-",1)
})
})
}
componentDidMount(){
let user = firebase.auth().currentUser;
console.log(user);
console.log('test');
this.getUserData(user.uid);
let images;
AsyncStorage.getItem('images')
.then(data => {
images = JSON.parse(data) || [];
this.setState({
images: images
});
})
.catch(error => {
console.log(error);
});
}
/**
* Select image method
*/
pickImage = () => {
ImagePicker.showImagePicker(options, response => {
if (response.didCancel) {
console.log('You cancelled image picker 😟');
} else if (response.error) {
alert('And error occured: ', response.error);
} else {
const source = { uri: response.uri };
this.setState({
imgSource: source,
imageUri: response.uri
});
console.log(source);
}
});
};
/**
* Upload image method
*/
uploadImage = () => {
const ext = this.state.imageUri.split('.').pop(); // Extract image extension
const filename = `unique image' + ${ext}`; // Generate unique name
const imageRef = firebase.storage().ref('tutorials/images').child(filename +ext);
let mime = 'image/jpg';
imageRef.put(this.state.imageUri, { contentType: mime }).then((snapshot)=>{
console.log('Image uploaded successfully.')
}).catch((error)=>{
console.log('Image uploading failed:' + error);
});
};
/**
* Remove image from the state and persistance storage
*/
removeImage = imageIndex => {
let images = this.state.images;
images.pop(imageIndex);
this.setState({ images });
AsyncStorage.setItem('images', JSON.stringify(images));
};
render() {
const { uploading, imgSource, progress, images } = this.state;
const windowWidth = Dimensions.get('window').width;
const disabledStyle = uploading ? styles.disabledBtn : {};
const actionBtnStyles = [styles.btn, disabledStyle];
return (
<ScrollView style={styles.scorllVert}>
<View style={{ alignItems: 'flex-start', justifyContent: 'center', marginBottom: 40 }}>
<View style={styles.intro}>
<Text style={styles.introText}>Hoi, ik ben {this.state.first}{"\n"}<Text style={styles.introTextSpan}>Lid geworden in {this.state.year}</Text></Text>
{ this.state.profileImg ?
<Avatar size="large" rounded source={{uri: this.state.profileImg,}} onPress={() => console.log("Works!")} activeOpacity={0.7} />
:
<Avatar size="large" rounded title="GLR" onPress={() => console.log("Works!")} activeOpacity={0.7} />
}
</View>
<View style={styles.divider}>
</View>
<View style={styles.about}>
<Text style={styles.profileName}>Over</Text>
<View style={styles.aboutLiving}>
<Icon name='home' style={styles.icon}/>
<Text style={styles.aboutText}>Woont in {this.state.place}, {this.state.province}</Text>
</View>
</View>
<View style={styles.divider}>
</View>
<View style={styles.about}>
<Text style={styles.profileName}>Door {this.state.first} versterkt</Text>
<View style={styles.aboutLiving}>
<Icon name='check-circle' style={[styles.icon, styles.iconGreen]}/>
<Text style={styles.aboutText}>E-mail adres</Text>
</View>
</View>
<View style={styles.divider}>
</View>
<View style={styles.about}>
<Text style={styles.profileName}>Recente activiteiten</Text>
<Text >N.v.t.</Text>
<TouchableOpacity
style={actionBtnStyles}
onPress={this.pickImage}
disabled={uploading}
>
<View>
<Text style={styles.btnTxt}>Pick image</Text>
</View>
</TouchableOpacity>
{imgSource !== '' && (
<View>
<Image source={imgSource} style={styles.image} />
{uploading && (
<View
style={[styles.progressBar, { width: `${progress}%` }]}
/>
)}
<TouchableOpacity
style={actionBtnStyles}
onPress={this.uploadImage}
disabled={uploading}
>
<View>
{uploading ? (
<Text style={styles.btnTxt}>Uploading ...</Text>
) : (
<Text style={styles.btnTxt}>Upload image</Text>
)}
</View>
</TouchableOpacity>
</View>
)}
<View>
<Text
style={{
fontWeight: '600',
paddingTop: 20,
alignSelf: 'center'
}}
>
{images.length > 0
? 'Your uploaded images'
: 'There is no image you uploaded'}
</Text>
</View>
<FlatList
numColumns={2}
style={{ marginTop: 20 }}
data={images}
renderItem={({ item: image, index }) => (
<ImageRow
windowWidth={windowWidth}
image={image}
popImage={() => this.removeImage(index)}
/>
)}
keyExtractor={index => index}
/>
</View>
</View>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
profileName:{
fontWeight: 'bold',
fontSize: 22,
marginTop: 20,
},
list:{
marginTop: 40,
width: '100%'
},intro:{
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'flex-start',
padding: 25,
marginBottom: 30,
paddingTop: 80,
},
introText:{
marginLeft: 0,
marginRight: 50,
fontSize: 22,
fontWeight: "700",
marginTop:15,
},
introTextSpan:{
marginLeft: 0,
marginRight: 40,
fontSize: 15,
fontWeight: "200",
marginTop:50,
},
divider:{
width: SCREEN_WIDTH-50,
padding: 10,
borderBottomColor: 'grey',
borderBottomWidth: 0.5,
marginTop: 10,
marginLeft: 25
},
about:{
paddingLeft: 25,
},
aboutLiving:{
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'flex-start',
paddingTop: 10
},
icon:{
fontSize: 18
},
aboutText:{
marginLeft: 30
},
iconGreen:{
color: 'green'
}
,
button: {
marginTop: 30,
marginBottom: 20,
paddingVertical: 10,
alignItems: 'center',
backgroundColor: '#019BB4',
width: 300
},
buttonText: {
fontSize: 20,
fontWeight: 'bold',
color: '#fff'
},
scorllVert:{
marginBottom: 40
},
btn: {
paddingLeft: 20,
paddingRight: 20,
paddingTop: 10,
paddingBottom: 10,
borderRadius: 20,
backgroundColor: 'rgb(3, 154, 229)',
marginTop: 20,
alignItems: 'center'
},
disabledBtn: {
backgroundColor: 'rgba(3,155,229,0.5)'
},
btnTxt: {
color: '#fff'
},
image: {
marginTop: 20,
minWidth: 200,
height: 200,
resizeMode: 'contain',
backgroundColor: '#ccc',
},
img: {
flex: 1,
height: 100,
margin: 5,
resizeMode: 'contain',
borderWidth: 1,
borderColor: '#eee',
backgroundColor: '#ccc'
},
progressBar: {
backgroundColor: 'rgb(3, 154, 229)',
height: 3,
shadowColor: '#000',
}
})
export default profileScreen;
I am fairly new to react native and would like to upload images, is there anyone who can help me get this done? Even if there are other methods, I am open to that!
Try wrapping your uploadImage function in a try..catch and see if you can get a clearer error message in catch
Try this code for uploading the image,
const ext = this.state.imageUri.split('.').pop(); // Extract image extension
const filename = `unique image' + ${ext}`; // Generate unique name
const imageRef = firebase.storage().ref('tutorials/images).child(filename +ext);
let mime = 'image/jpg';
imageRef.put(this.state.imageUri, { contentType: mime }).then((snapshot)=>{
console.log('Image uploaded successfully.')
}).catch((error)=>{
console.log('Image uploading failed');
});

Type error : cannot read property from mapstatetoprops

I am new to react-native
In reducer I set the initial value of the state.
while receiving the value in mapstate to props, it gives me the error
Type error : cannot read property of datasource.
I tried to set initial state to blank object then on fetch I set the value but I am still getting the error.
/* This is Dashboard.js */
import React, { Component } from 'react'
import { FlatList, StyleSheet, View, Text,Image,ActivityIndicator,
Picker,TouchableOpacity,SectionList,DrawerLayoutAndroid,Alert,
TouchableHighlight,Button} from 'react-native'
import NumericInput from 'react-native-numeric-input'
import {connect} from 'react-redux';
import {
createDrawerNavigator,
createStackNavigator,
createAppContainer,
} from 'react-navigation';
const mapStateToProps = (state) =>{
console.log(state)
return {
datasource:state.datasource,
isLoading:state.isLoading,
switchValue: state.switchValue,
page:state.page,
rerender:state.rerender,
isFetching: state.isFetching,
search:state.search,
LastSelectedItem:state.LastSelectedItem,
ItemSelected:state.ItemSelected,
setdrawerstate:state.setdrawerstate
}
};
const mapDispatchToProps=(dispatch)=>{
console.log('i am ni map dispatch to props')
return {
quantitychange:(prodindex, changevalue)=>dispatch({type:"CHANGE_QTY",
index:prodindex,
value:changevalue}),
selectvaluechange:(index,itemvalue,itemindex)=>dispatch({type:"CHANGE_WEIGHT",
index:index,
itemvalue:itemvalue,
itemindex:itemindex}),
ToogleDrawerState:(newState)=>dispatch({type:'TOOGLE_DRAWER',newState:newState}),
taggleSwitch:(value,ind)=>dispatch({type:'TOOGLE_SWITCH',value:value,ind:ind}),
fetchAllData:(AlldataJson)=>dispatch({type:'ALL_DATA',AlldataJson:AlldataJson})
};
};
class DashBoard extends Component {
constructor(props) {
super(props)
console.log('i am in constructor')
console.log('the value getting as props is '+JSON.stringify(props));
// this.selectvaluechange=this.selectvaluechange.bind(this);
// this._opendrawer = this._opendrawer.bind(this);
} //constructor
packageList = (index) =>{
// console.log('i am in package list'+index)
return( this.props.datasource[index].pack.map( (x,j) => {
return( <Picker.Item label={x.Weight} key={j} value={x.packId}/>)}));
}
// Verdana 20
renderItem = ({ item,index }) => {
// console.log('i am in render item index is '+index+' item vale is '+JSON.stringify(item));
return(
<View style={{
flex: 1, flexDirection: 'row', marginBottom: 3,
paddingBottom: 10, paddingTop:10, paddingLeft: 10, backgroundColor: 'white', height: '25%'
}}>
<View style={{ width: '30%' }}>
<Image style={{ resizeMode: 'contain', height: '100%',width:'100%' }}
source={{ uri: this.props.datasource[index].imageUrl }} />
</View>
<View style={{ alignContent: 'center', paddingLeft: 40, width: '65%' }}>
<Text style={{
fontSize: 20, color: 'black', fontWeight: 'bold',
fontFamily: 'Verdana'
}}>
{item.Heading}</Text>
<Text style={{
fontSize: 12, paddingTop: 10, fontFamily: 'Verdana',
color: 'black', fontWeight: 'bold'
}}>{item.Details}</Text>
<View style={{ flex: 1, flexDirection: 'row', paddingTop: 15 }}>
<Text style={{ width: 40, height: 30, fontFamily: 'Verdana', fontWeight: 'bold' }}>Qty :</Text>
<NumericInput
totalHeight={30}
totalWidth={80}
minValue={0}
value={this.props.datasource[index].Qty}
onChange={(value) => this.props.quantitychange(index, value)} />
<Picker
style={{ marginLeft: 10, width: '45%', height: 40, fontFamily: 'Verdana', fontWeight: 'bold' }}
mode="dropdown"
selectedValue={this.props.datasource[index].packselectedValue}
onValueChange={(itemValue, itemIndex) => this.props.selectvaluechange(index, itemValue, itemIndex)}>
{this.packageList(index)}
</Picker>
</View>
<View >
<Text style={{ fontFamily: 'Verdana', fontWeight: 'bold' }}>Price : {this.props.datasource[index].
pack[this.props.datasource[index].packselectedValue].Price}
<Text style={{ fontFamily: 'Verdana', fontWeight: 'bold' }}> Amount : {this.props.datasource[index].
pack[this.pack.datasource[index].packselectedValue].Price *
this.props.datasource[index].Qty}
</Text>
</Text>
</View>
</View>
</View>
)
}
renderSeperator=()=>{
console.log('I am renderseperator the data ')
return(
<View style={{height:1,width:'100%',backgroundColor:'black'}}/>
)
}
onRefresh=()=>{
console.log('I am refreshing the data '+this.props)
// this.setState({isFetching:false})
}
componentDidMount() {
AlldataJson = [{}];
console.log('i am in component didmount ');
const url='./../Data/AllProduct.json'
// ./../Data/AllProduct.json'
fetch(url)
.then((response) => response.json())
.then((responsejson => {
console.log('i am in response json')
fetchAllData(responsejson)}))
//this.setState({ datasource: responsejson })})
.catch((error) => {
console.log('error in fetching the data bhasker')
this.props.fetchAllData(AlldataJson);
console.log('i am after fetchalldata function'+this.props)
})
this.props.navigation.setParams({ opendrawer: this._opendrawer});
}
// data={this.state.page===1?this.state.datasource:[this.state.datasource]}
render() {
return (
this.props.isLoading ?
<View style={{ flex: 1, justifyContent: 'center', alignContent: 'center' }}>
<ActivityIndicator size='large' color='#330066' animating />
</View> : <View>
{/* <Text>{this.props.datasource[0].Heading}</Text> */}
<FlatList
extraData={this.props.state}
data={this.props.datasource}
onRefresh={() => this.onRefresh()}
refreshing={this.props.isFetching}
renderItem={(item,index)=>this.renderItem(item,index)}
keyExtractor={(item, index) => {
console.log('i am in keyextractor')
return (item.Id)}}
ItemSeparatorComponent={this.renderSeperator}/>
</View>
)
}
}
export default connect(mapStateToProps,mapDispatchToProps)(DashBoard);
/* My Reducer code is */
import * as types from '../Action/ActionType'
import { objectExpression } from '#babel/types';
import Allproduct from './../Data/AllProduct';
const initialstate={
datasource: Allproduct,
isLoading:false,
switchValue: true,
page:1,
rerender:true ,
isFetching: false,
search:"" ,
LastSelectedItem:{Item:"",qty:"",weight:""},
ItemSelected:0,
setdrawerstate:true
}
const reducer = (state = initialstate, action) => {
console.log('i am in reducer '+JSON.stringify(action));
switch (action.type) {
case "CHANGE_QTY":
break;
case "CHANGE_WEIGHT":
break;
case "TOOGLE_DRAWER":
break;
case "TOOGLE_SWITCH":
break;
case "ALL_DATA":
console.log('i am in all data') ;
return state;
default:
return state;
}
}
export default reducer;
/* if i comment datasource in mapstatetoprops it print the text value.
Allproduct json is below
*/
[{"Id":"1","Heading":"Ashirvad","Details": "ashirvad Atta - Whole Wheet 10k Pouch","imageUrl": "https://www.aashirvaad.com/images/packet-2-right.png", "Qty": 0,"pack": [{"packId":0,"Weight": "1kg","Price": 25}, {"packId":1,"Weight": "2kg","Price": 50}, {"packId":2,"Weight": "5kg","Price": 125}, {"packId":3,"Weight": "10kg","Price": 250}], "packselectedValue":0,"isselected": false,"categoryID":"1"}, {"Id":"2","Heading": "Tata Salt","Details": "Tata Salt Iodised Salt 1kg Product","imageUrl": "http://tatasalt.com/public/front_assets/images/tab-product-img1.png","Qty": 0,"pack": [{"packId":0,"Weight": "1kg","Price": 9.5},{"packId":1,"Weight": "2kg","Price": 19}, {"packId":2,"Weight": "5kg","Price": 47 },{"packId":3,"Weight": "10kg","Price": 92}],"packselectedValue":0,"isselected": false,"categoryID":"1"},

flat list error on fetching next page data

I am using redux to get the data from flatlist, in render item function i can see the data is rendering after fetching the page record it throw error
Invariant violation : tried to get frame out of range index 0
in debug statement in renderitem i can see the data is populated and it is printing the data in console. Below is my code
import React, { Component } from 'react'
import { FlatList, StyleSheet, View, Text,Image,ActivityIndicator,
Picker,TouchableOpacity,SectionList,DrawerLayoutAndroid,Alert,
TouchableHighlight,Button} from 'react-native'
import NumericInput from 'react-native-numeric-input'
import {connect} from 'react-redux';
import AlldataJson from './../Data/AllProduct.json'
import {
createDrawerNavigator,
createStackNavigator,
createAppContainer,
} from 'react-navigation';
const mapStateToProps = (state) =>{
// console.log('I am in mapstate to props '+ JSON.stringify(state));
return {
datasource:state.datasource,
isLoading:state.isLoading,
switchValue: state.switchValue,
page:state.page,
rerender:state.rerender,
isFetching: state.isFetching,
search:state.search,
LastSelectedItem:state.LastSelectedItem,
ItemSelected:state.ItemSelected,
setdrawerstate:state.setdrawerstate
}
};
const mapDispatchToProps=(dispatch)=>{
// console.log('i am ni map dispatch to props')
return {
quantitychange:(prodindex, changevalue)=>dispatch({type:"CHANGE_QTY",
index:prodindex,
value:changevalue}),
selectvaluechange:(index,itemvalue,itemindex)=>dispatch({type:"CHANGE_WEIGHT",
index:index,
itemvalue:itemvalue,
itemindex:itemindex}),
ToogleDrawerState:(newState)=>dispatch({type:'TOOGLE_DRAWER',newState:newState}),
taggleSwitch:(value,ind)=>dispatch({type:'TOOGLE_SWITCH',value:value,ind:ind}),
fetchAllData:(AlldataJson)=>dispatch({type:'ALL_DATA',AlldataJson:AlldataJson})
};
};
class DashBoard extends Component {
constructor(props) {
super(props)
console.log('i am in constructor')
console.log('the value getting as props is '+JSON.stringify(props));
// this.selectvaluechange=this.selectvaluechange.bind(this);
// this._opendrawer = this._opendrawer.bind(this);
} //constructor
packageList = (index) =>{
// console.log('i am in package list'+index)
return( this.props.datasource[index].pack.map( (x,j) => {
return( <Picker.Item label={x.Weight} key={j} value={x.packId}/>)}));
}
// Verdana 20
renderItem = ({ item,index }) => {
// console.log('i am in render item index is '+index+' item vale is '+JSON.stringify(item));
return(
<View style={{
flex: 1, flexDirection: 'row', marginBottom: 3,
paddingBottom: 10, paddingTop:10, paddingLeft: 10, backgroundColor: 'white', height: '25%'
}}>
<View style={{ width: '30%' }}>
<Image style={{ resizeMode: 'contain', height: '100%',width:'100%' }}
source={{ uri: this.props.datasource[index].imageUrl }} />
</View>
<View style={{ alignContent: 'center', paddingLeft: 40, width: '65%' }}>
<Text style={{
fontSize: 20, color: 'black', fontWeight: 'bold',
fontFamily: 'Verdana'
}}>
{item.Heading}</Text>
<Text style={{
fontSize: 12, paddingTop: 10, fontFamily: 'Verdana',
color: 'black', fontWeight: 'bold'
}}>{item.Details}</Text>
<View style={{ flex: 1, flexDirection: 'row', paddingTop: 15 }}>
<Text style={{ width: 40, height: 30, fontFamily: 'Verdana', fontWeight: 'bold' }}>Qty :</Text>
<NumericInput
totalHeight={30}
totalWidth={80}
minValue={0}
value={this.props.datasource[index].Qty}
onChange={(value) => this.props.quantitychange(index, value)} />
<Picker
style={{ marginLeft: 10, width: '45%', height: 40, fontFamily: 'Verdana', fontWeight: 'bold' }}
mode="dropdown"
selectedValue={this.props.datasource[index].packselectedValue}
onValueChange={(itemValue, itemIndex) => this.props.selectvaluechange(index, itemValue, itemIndex)}>
{this.packageList(index)}
</Picker>
</View>
<View >
<Text style={{ fontFamily: 'Verdana', fontWeight: 'bold' }}>Price : {this.props.datasource[index].
pack[this.props.datasource[index].packselectedValue].Price}
<Text style={{ fontFamily: 'Verdana', fontWeight: 'bold' }}> Amount : {this.props.datasource[index].
pack[this.pack.datasource[index].packselectedValue].Price *
this.props.datasource[index].Qty}
</Text>
</Text>
</View>
</View>
</View>
)
}
renderSeperator=()=>{
console.log('I am renderseperator the data ')
return(
<View style={{height:1,width:'100%',backgroundColor:'black'}}/>
)
}
onRefresh=()=>{
console.log('I am refreshing the data '+this.props)
// this.setState({isFetching:false})
}
componentDidMount() {
console.log('i am in component didmount ');
const url='./../Data/AllProduct.json'
// ./../Data/AllProduct.json'
fetch(url)
.then((response) => response.json())
.then((responsejson => {
console.log('i am in response json')
fetchAllData(responsejson)}))
//this.setState({ datasource: responsejson })})
.catch((error) => {
console.log('error in fetching the data bhasker')
this.props.fetchAllData(AlldataJson);
console.log('i am after fetchalldata function'+this.props)
})
this.props.navigation.setParams({ opendrawer: this._opendrawer});
}
// data={this.state.page===1?this.state.datasource:[this.state.datasource]}
render() {
// console.log('I am in render '+JSON.stringify(this.props.datasource));
return (
this.props.isLoading ?
<View style={{ flex: 1, justifyContent: 'center', alignContent: 'center' }}>
<ActivityIndicator size='large' color='#330066' animating />
</View> : <View>
<FlatList data={this.props.datasource}
renderItem={(item,index)=>this.renderItem(item,index)}
keyExtractor={item=>item.Id}
debug='yes'/>
</View>
)
}
}
export default connect(mapStateToProps,mapDispatchToProps)(DashBoard);
/* Reducer is */
import * as types from '../Action/ActionType'
import { objectExpression } from '#babel/types';
import Allproduct from './../Data/AllProduct';
const initialstate={
datasource: [],
isLoading:false,
switchValue: true,
page:1,
rerender:true ,
isFetching: false,
search:"" ,
LastSelectedItem:{Item:"",qty:"",weight:""},
ItemSelected:0,
setdrawerstate:true
}
const reducer = (state = initialstate, action) => {
// console.log('i am in reducer '+JSON.stringify(action));
switch (action.type) {
case "CHANGE_QTY":
break;
case "CHANGE_WEIGHT":
break;
case "TOOGLE_DRAWER":
break;
case "TOOGLE_SWITCH":
break;
case "ALL_DATA":
console.log('i am in all data') ;
return {...state,
datasource:action.AlldataJson};
default:
return state;
}
}
export default reducer;
/* All product json is */
[{"Id":"1","Heading":"Ashirvad","Details": "ashirvad Atta - Whole Wheet 10k Pouch",
"imageUrl": "https://www.aashirvaad.com/images/packet-2-right.png", "Qty": 0,
"pack": [{"packId":0,"Weight": "1kg","Price": 25},
{"packId":1,"Weight": "2kg","Price": 50},
{"packId":2,"Weight": "5kg","Price": 125},
{"packId":3,"Weight": "10kg","Price": 250}],
"packselectedValue":0,"isselected": false},
{"Id":"2","Heading": "Tata Salt","Details": "Tata Salt Iodised Salt 1kg Product",
"imageUrl": "http://tatasalt.com/public/front_assets/images/tab-product-img1.png",
"Qty": 0,
"pack": [{"packId":0,"Weight": "1kg","Price": 9.5},{"packId":1,"Weight": "2kg","Price": 19}, {"packId":2,"Weight": "5kg","Price": 47 },
{"packId":3,"Weight": "10kg","Price": 92}],"packselectedValue":0,"isselected": false}]
This is the error i am getting
It occurs because you have debug='yes' for the FlatList, and its data is initially an empty array. To remove the error, either of these should work:
remove the debug prop
add some dummy element to the initial data
render some other component (like a View) if the data array doesn't have any elements
Btw the function parameters/arguments in this line of FlatList
renderItem={(item,index)=>this.renderItem(item,index)}
should have curly braces ({})
renderItem={({item,index})=>this.renderItem({item,index})}
Or better yet, just change it to renderItem={this.renderItem}
Please accept this answer if it solved the problem. Thanks :)
Thanks Amrut Prabha for the help
the problem is in the below code.
<Text style={{ fontFamily: 'Verdana', fontWeight: 'bold' }}> Amount : {this.props.datasource[index].
pack[this.pack.datasource[index].packselectedValue].Price *
this.props.datasource[index].Qty}
pack[this.pack.datasource[index].packselectedValue.price is wrong..
pack[this.props.datasource[index].packselectedValue.price is correct.
i fixed the issue it solve the issue.

Conditional rendering in react native using map function

I am trying to build a notification page where notifications are being fetched from a remote server using the Notification function, so I initially set the notificationLoaded to false in order to use an ActivityLoader before rendering the notification to the page.
But I am confused about how I can render an ActivityLoader before the notificationLoaded state is set to true.
Thanks in advance
import React, { Component } from 'react';
import {
StyleSheet,
ScrollView,
Dimensions,
Text,
ActivityIndicator,
TouchableOpacity,
TextInput,
View,
StatusBar,
ImageBackground,
KeyboardAvoidingView,
AsyncStorage,
} from 'react-native';
import { Ionicons } from '#expo/vector-icons';
import { Font } from 'expo';
import { createStackNavigator, createAppContainer } from 'react-navigation';
import { KeyboardAwareView } from 'react-native-keyboard-aware-view';
const { height, width } = Dimensions.get('window');
export default class Notification extends Component {
constructor(props) {
super(props);
this.state = {
notification: [],
notificationLoaded: false,
};
}
Notification = () => fetch('http://texotrack.com/api/user/notification.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'fetch',
}),
}).then(response => response.json()).then((responseJsonFromServer) => {
console.log(responseJsonFromServer);
this.setState({
notification: responseJsonFromServer,
notificationLoaded: true,
});
})
componentDidMount() {
this.Notification();
AsyncStorage.getItem('key').then((data) => {
const val = JSON.parse(data);
this.setState({
username: data.name,
photo: data.photo,
email: data.email,
userId: data.id,
address: data.address,
});
});
}
render() {
const notificationList = this.state.notification.map(data => (
<View key={data.msg_id} style={styles.notification}>
<View style={{
display: 'flex', flexDirection: 'row',
}}
>
<Text style={{ color: 'green' }}><Ionicons size={20} color="green" name="ios-notifications" /></Text>
<Text style={{
color: '#c9c9c9', fontSize: 15, fontWeight: 'bold', marginLeft: 5,
}}
>
{data.date_sent}
</Text>
</View>
<View style={styles.notificationBody}>
<Text style={{ color: '#000', fontSize: 16 }}>{data.message}</Text>
</View>
<View style={styles.lineStyle} />
</View>
));
return (
<View style={styles.container}>
<StatusBar
style={{ height: 30 }}
backgroundColor="black"
/>
<View elevation={5} style={styles.headers}>
<Text style={{
fontSize: 25, color: 'green', textTransform: 'uppercase', fontWeight: 'bold',
}}
>
Notification
</Text>
<Text style={styles.topIcon} onPress={this.GoHome}>
<Ionicons name="md-home" size={25} color="black" />
</Text>
</View>
<View style={{ flex: 1, margin: 5 }}>
<ScrollView alwaysBounceVertical contentContainerStyle={{ flexGrow: 1 }} enabled bounces>
{notificationList}
</ScrollView>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
lineStyle: {
borderWidth: 0.3,
borderColor: '#c9c9c9',
margin: 10,
},
headers: {
height: 50,
marginBottom: 10,
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
textAlignVertical: 'center',
padding: 10,
shadowColor: '#444',
shadowOffset: {
width: 0,
height: 60,
},
shadowRadius: 5,
shadowOpacity: 1.0,
backgroundColor: '#fff',
},
topIcon: {
marginTop: 3,
fontWeight: 'bold',
},
content: {
margin: 10,
flex: 1,
},
notification: {
height: 70,
padding: 10,
},
});
This is what you call conditional rendering. This can be achieved simply by using if conditions in your render function like:
render(){
// if loading true render Loader
if (this.state.notificationStateLoaded === true) {
return(
<ActivityLoader>
)
} else { // when loading false render the other component
return(
<WhateverComponentWhenDataHasArrived/>
)
}
}
React's documentation is pretty cool. Check this official link for conditional rendering
https://reactjs.org/docs/conditional-rendering.html
I dont understand what you need to do in your map function but to do a conditional rendering inside your render you can do :
<View>
{someCondition&& <Text>Hello</Text>}
</View>
else
<View>
{someCondition? <Text>Condition True</Text> : <Text>Condition false</Text>}
</View>