flat list error on fetching next page data - react-native

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.

Related

How to refresh the navigation parameters when there are more card components to navigate in React Native

When I click one card component and pass the data to the other page, and when I go back and click another one parameter that I pass from navigation, the navigate does not get changed. Here is my first page to navigate from:
import React,{ useState,useEffect} from 'react';
import { View, Text,TouchableOpacity, Button, StyleSheet ,Image,ScrollView} from 'react-native';
import { FontAwesome, Feather, MaterialIcons,Ionicons } from 'react-native-vector-icons';
const PaymentScreen = ({ route, navigation }) => {
const [paymentList, setPaymentList] = useState([]);
useEffect(() => {
fetch(
"https://run.mocky.io/v3/73958238-7761-4d81-8577-793ff92c0ea1"
)
.then((res) => res.json())
.then((data) => {
setPaymentList(data);
});
}, []);
const showPayment=() =>{
return (
paymentList &&
paymentList
.filter((word) => route.params.Name== word.userID)
.map((Aname, i) => {
return (
<View style={{padding: 8 }}>
<PaymenCard date={Aname.date} discount={Aname.id} cash={Aname.amount} forwardLink={() => navigation.navigate('morePayments',{itemId: Aname.id})}/>
</View>
);
})
)
}
return (
<View style={styles.container}>
<View style={styles.paymentbox}>
<Text style={styles.payment}>Payments</Text>
</View>
<ScrollView>
{ showPayment()}
</ScrollView>
</View>
);
};
export default PaymentScreen;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor:'#22343C',
},
payment:{
fontSize:42,
color: "white",
fontWeight: "bold",
},
paymentbox:{
marginLeft:32,
paddingBottom: 5
}
});
function PaymenCard(props ){
return(
<TouchableOpacity onPress={props.forwardLink}>
<View style={styles1.cardbox}>
<View style={styles1.square}>
<View style={styles1.dollarbig}>
<MaterialIcons name="monetization-on" color="#FFC542" size={30} />
</View>
</View>
<View style={styles1.datedisc}>
<View style={styles1.date}>
<Text style={styles1.datetext}>{props.date} </Text>
</View>
<View style={styles1.discount}>
<View style={styles1.rocket}>
<FontAwesome name="rocket" color="#FFC542" size={20} />
</View>
<View style={styles1.discountval}>
<Text style={styles1.discounttext}>{props.discount}</Text>
</View>
</View>
</View>
<View style={styles1.cashbox}>
<Text style={styles1.cashtext}>{props.cash}</Text>
</View>
</View>
</TouchableOpacity>
)
}
const styles1 = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#22343C',
},
cardbox: {
height: 115,
width: 345,
backgroundColor: '#30444E',
borderRadius: 25,
marginLeft: 22,
flexDirection: 'row',
},
square: {
height: 57,
width: 59,
borderRadius: 12,
backgroundColor: '#FF565E',
marginTop: 24,
marginLeft: 23,
},
dollarbig: {
marginTop: 15,
alignItems: 'center',
},
datedisc: {
marginTop: 24,
marginLeft: 16,
width: 145,
flexDirection:"column"
},
datetext: {
color: 'white',
fontSize: 14,
},
discount:{
marginTop:15,
flexDirection:"row",
},
rocket:{
},
discountval:{
marginLeft:13
},
discounttext:{
color:"white",
fontSize: 14,
},
cashbox:{
marginTop:30,
marginLeft:25
},
cashtext:{
color:"#FF575F",
fontWeight:"bold",
fontSize:18
}
});
I found the solution. there I have used initial params. so once the value is set it not going to upgrade again. I used react context API to set the updating values globally

Showing search icon inside react-native-autocomplete-input component

I am using the react-native-autocomplete-input package for auto-complete searching.
https://www.npmjs.com/package/react-native-autocomplete-input
Here is a template code which works:
//Example of React Native AutoComplete Input
//https://aboutreact.com/example-of-react-native-autocomplete-input/
//import React in our code
import React, {useState, useEffect} from 'react';
//import all the components we are going to use
import {
SafeAreaView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
//import Autocomplete component
import Autocomplete from 'react-native-autocomplete-input';
const App = () => {
const [films, setFilms] = useState([]); // For the main data
const [filteredFilms, setFilteredFilms] = useState([]); // Filtered data
const [selectedValue, setSelectedValue] = useState({}); // selected data
useEffect(() => {
fetch('https://aboutreact.herokuapp.com/getpost.php?offset=1')
.then((res) => res.json())
.then((json) => {
const {results: films} = json;
setFilms(films);
//setting the data in the films state
})
.catch((e) => {
alert(e);
});
}, []);
const findFilm = (query) => {
//method called everytime when we change the value of the input
if (query) {
//making a case insensitive regular expression
const regex = new RegExp(`${query.trim()}`, 'i');
//setting the filtered film array according the query
setFilteredFilms(
films.filter((film) => film.title.search(regex) >= 0)
);
} else {
//if the query is null then return blank
setFilteredFilms([]);
}
};
return (
<SafeAreaView style={{flex: 1}}>
<View style={styles.container}>
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.autocompleteContainer}
//data to show in suggestion
data={filteredFilms}
//default value if you want to set something in input
defaultValue={
JSON.stringify(selectedValue) === '{}' ?
'' :
selectedValue.title
}
// onchange of the text changing the state of the query
// which will trigger the findFilm method
// to show the suggestions
onChangeText={(text) => findFilm(text)}
placeholder="Enter the film title"
renderItem={({item}) => (
//you can change the view you want to show in suggestions
<TouchableOpacity
onPress={() => {
setSelectedValue(item);
setFilteredFilms([]);
}}>
<Text style={styles.itemText}>
{item.title}
</Text>
</TouchableOpacity>
)}
/>
<View style={styles.descriptionContainer}>
{films.length > 0 ? (
<>
<Text style={styles.infoText}>
Selected Data
</Text>
<Text style={styles.infoText}>
{JSON.stringify(selectedValue)}
</Text>
</>
) : (
<Text style={styles.infoText}>
Enter The Film Title
</Text>
)}
</View>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: '#F5FCFF',
flex: 1,
padding: 16,
marginTop: 40,
},
autocompleteContainer: {
backgroundColor: '#ffffff',
borderWidth: 0,
},
descriptionContainer: {
flex: 1,
justifyContent: 'center',
},
itemText: {
fontSize: 15,
paddingTop: 5,
paddingBottom: 5,
margin: 2,
},
infoText: {
textAlign: 'center',
fontSize: 16,
},
});
export default App;
The input box looks like below.
I want a search icon at the right/left inside of the search input box.
Is there any way I can do this?
Here is the working code with
//Example of React Native AutoComplete Input
//https://aboutreact.com/example-of-react-native-autocomplete-input/
//import React in our code
import React, {useState, useEffect} from 'react';
import AntDesign from 'react-native-vector-icons/AntDesign';
//import all the components we are going to use
import {
SafeAreaView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
//import Autocomplete component
import Autocomplete from 'react-native-autocomplete-input';
const App = () => {
const [films, setFilms] = useState([]); // For the main data
const [filteredFilms, setFilteredFilms] = useState([]); // Filtered data
const [selectedValue, setSelectedValue] = useState({}); // selected data
useEffect(() => {
fetch('https://aboutreact.herokuapp.com/getpost.php?offset=1')
.then((res) => res.json())
.then((json) => {
const {results: films} = json;
setFilms(films);
//setting the data in the films state
})
.catch((e) => {
alert(e);
});
}, []);
const findFilm = (query) => {
//method called everytime when we change the value of the input
if (query) {
//making a case insensitive regular expression
const regex = new RegExp(`${query.trim()}`, 'i');
//setting the filtered film array according the query
setFilteredFilms(
films.filter((film) => film.title.search(regex) >= 0)
);
} else {
//if the query is null then return blank
setFilteredFilms([]);
}
};
return (
<View style={{flex: 1}}><View><Text>Hello Friend</Text></View>
<View style={styles.container}>
<View style={styles.searchSection}>
<AntDesign
name="search1"
size={18}
color="gray"
style={styles.searchIcon}
/>
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.autocompleteContainer}
inputContainerStyle={styles.inputContainer}
//data to show in suggestion
data={filteredFilms}
//default value if you want to set something in input
defaultValue={
JSON.stringify(selectedValue) === '{}'
? ''
: selectedValue.title + selectedValue.id
}
// onchange of the text changing the state of the query
// which will trigger the findFilm method
// to show the suggestions
onChangeText={(text) => findFilm(text)}
placeholder="Search doctors, specialities, symptoms"
renderItem={({item}) => (
//you can change the view you want to show in suggestions
<View>
<TouchableOpacity
onPress={() => {
setSelectedValue(item);
setFilteredFilms([]);
}}>
<Text style={styles.itemText}>{item.title + item.id}</Text>
</TouchableOpacity>
</View>
)}
/>
<AntDesign
name="close"
size={18}
color="gray"
style={styles.clearIcon}
/>
</View>
<View style={styles.descriptionContainer}>
{films.length > 0 ? (
<>
<Text style={styles.infoText}>
Selected Data
</Text>
<Text style={styles.infoText}>
{JSON.stringify(selectedValue)}
</Text>
</>
) : (
<Text style={styles.infoText}>
Enter The Film Title
</Text>
)}
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: '#F5FCFF',
flex: 1,
padding: 16,
marginTop: 40,
},
autocompleteContainer: {
backgroundColor: '#ffffff',
borderWidth: 0,
marginLeft: 10,
marginRight: 10,
//paddingLeft: 15,
},
inputContainer: {
//minWidth: 300,
//width: "90%",
//height: 55,
backgroundColor: 'transparent',
//color: '#6C6363',
//fontSize: 18,
//fontFamily: 'Roboto',
borderBottomWidth: 1,
//borderBottomColor: 'rgba(108, 99, 99, .7)',
borderColor: 'transparent',
},
descriptionContainer: {
flex: 1,
justifyContent: 'center',
},
itemText: {
fontSize: 15,
paddingTop: 5,
paddingBottom: 5,
margin: 2,
},
infoText: {
textAlign: 'center',
fontSize: 16,
},
// testing below
searchSection: {
flex: 1,
height: 50,
borderRadius: 10,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginLeft: '5%',
marginRight: '5%',
backgroundColor: '#fff',
},
searchIcon: {
//padding: 10,
paddingLeft: 10,
backgroundColor: 'transparent',
},
clearIcon: {
paddingRight: 10,
backgroundColor: 'transparent',
},
});
export default App;
npm install react-native-vector-icons for the AntDesign icons.
I am using "react-native-vector-icons": "^7.1.0".
Your output will be like:
Have a great day!!

Updates State when SectionList has finished rendering

I have a sectionlist with an ActivityIndicator at the footer. The ActivityIndicator doesn't go away even after the list has rendered every item. The data is local data.
I do know that I have to use React Hook, so I created _handleLoadMore() to update the state, but I don't know how to detect once the list has come to the end.
This is my code
import React, {useState, useEffect} from 'react';
import {
View,
Text,
StyleSheet,
SectionList,
ActivityIndicator,
} from 'react-native';
import {SafeAreaView} from 'react-native-safe-area-context';
export default function TransactionHistoryList({data}: {data: any}) {
const [loadMore, setLoadMore] = useState('true')
const _handleLoadMore = () => {
//what to put here
}
const extractKey = ({
id,
}: {
id: any;
name: any;
accountNumber: any;
type: any;
amount: any;
}) => id;
const renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '94%',
backgroundColor: '#000',
alignSelf: 'center',
}}
/>
);
};
const footerComponent = () => {
if (!_handleLoadMore) return null;
return (
<View
style={{
position: 'relative',
paddingVertical: 20,
borderTopWidth: 1,
marginTop: 10,
marginBottom: 10,
}}>
<ActivityIndicator
animating
size="small"
color="#CED0CE"
hidesWhenStopped={true}
/>
</View>
);
};
return (
<SafeAreaView>
<View style={styles.container}>
<Text style={styles.transactionhistory}>Transaction History</Text>
<SectionList
contentContainerStyle={{paddingBottom: 500}}
maxToRenderPerBatch={7}
onEndReachedThreshold={2}
updateCellsBatchingPeriod={4000}
ItemSeparatorComponent={renderSeparator}
sections={data}
renderSectionHeader={({section}) => {
return <Text style={styles.date}>{section.title}</Text>;
}}
renderItem={({item}) => {
return (
<View style={styles.item}>
<Text>
<View>
<Text style={styles.name}>{item.name}</Text>
<Text style={styles.accountNumber}>
{item.accountNumber}
</Text>
</View>
</Text>
<View style={styles.amountContainer}>
<Text
style={[
item.type == 'in' ? styles.moneyIn : styles.moneyOut,
]}>
{item.amount}
</Text>
</View>
</View>
);
}}
ListFooterComponent= {footerComponent}
keyExtractor={extractKey}
/>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
marginLeft: 20,
marginRight: 20,
marginTop: 120,
flex: -200,
//paddingBottom: 10
},
transactionhistory: {
fontWeight: 'bold',
fontSize: 18,
paddingBottom: 10,
paddingLeft: 25,
},
item: {
flexDirection: 'row',
justifyContent: 'space-between',
marginRight: 20,
marginLeft: 25,
paddingBottom: 10,
paddingTop: 10,
},
date: {
padding: 10,
marginBottom: 15,
backgroundColor: '#e0e0e0',
fontFamily: 'OpenSans-Bold',
fontSize: 15,
paddingLeft: 25,
},
name: {
fontSize: 14,
fontFamily: 'OpenSans-SemiBold',
},
accountNumber: {
fontSize: 12,
fontFamily: 'OpenSans-Regular',
},
amountContainer: {
paddingTop: 8,
},
moneyIn: {
color: '#689f38',
letterSpacing: 0.8,
fontSize: 16,
fontFamily: 'OpenSans-SemiBold',
},
moneyOut: {
color: '#b71c1c',
letterSpacing: 0.8,
fontSize: 16,
fontFamily: 'OpenSans-SemiBold',
},
loading: {
color: '#CED0CE',
},
});
section-list support on-scroll event and you can can hide/show activity-indicator feature quite smoothly with on scroll
Example:
const isCloseToBottom = ({layoutMeasurement, contentOffset, contentSize}) => {
const paddingToBottom = 20;
return layoutMeasurement.height + contentOffset.y >=
contentSize.height - paddingToBottom;
};
<SectionList
contentContainerStyle={{paddingBottom: 500}}
maxToRenderPerBatch={7}
onEndReachedThreshold={2}
onScroll={({nativeEvent}) => {
if (isCloseToBottom(nativeEvent)) {
if(!this.state.fetching_status){
//what you want to do when the screen reached end of screen
//console.log or something usefull
}
}
}}
.../>
I think it is possible to use onEndReached prop of SectionList in your case https://reactnative.dev/docs/sectionlist#onendreached
That is the correct place to write your logic to load next "page" of your items

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"},

React Redux Saga API call

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)]).