How to prevent state reset while navigating between screens? react native - react-native

In react native app, I have a home screen and a second screen that the user uses to add items that should be displayed on the home screen. The problem is when I add items in the second screen and go to the home screen I can see the added items but when I go again to the second screen to add other items, it deletes the previously added items.
Any help to explain why this happens and how to handle it?
Thanks in advance
Here's the code of the app.
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="AddItem" component={AddItem} />
</Stack.Navigator>
</NavigationContainer>
);
}
and here's the home screen component
class Home extends Component {
render() {
const { expenseList } = this.props.route.params || '';
return (
<View style={styles.container}>
<Text style={styles.text}>Budget:</Text>
<Button
title="+"
onPress={() => this.props.navigation.navigate('AddItem')}
/>
<View>
{expenseList === '' && (
<TouchableOpacity
onPress={() => this.props.navigation.navigate('AddItem')}>
<Text>Create your first entry</Text>
</TouchableOpacity>
)}
{expenseList !== '' && (
<FlatList
style={styles.listContainer}
data={expenseList}
renderItem={(data) => <Text> title={data.item.name} </Text>}
/>
)}
</View>
</View>
);
}
}
and the second screen
class AddItem extends Component {
state = {
name: '',
amount: '',
expenseList: [],
};
submitExpense = (name, amount) => {
this.setState({
expenseList: [
...this.state.expenseList,
{
key: Math.random(),
name: name,
amount: amount,
},
],
});
};
deleteExpense = (key) => {
this.setState({
expenseList: [
...this.state.expenseList.filter((item) => item.key !== key),
],
});
};
render() {
return (
<View style={styles.container}>
<TextInput
style={styles.input}
onChangeText={(name) => this.setState({ name })}
value={this.state.name}
placeholder="Name"
keyboardType="default"
/>
{this.state.name === '' && (
<Text style={{ color: 'red', fontSize: 12, paddingLeft: 12 }}>
Name is required
</Text>
)}
<TextInput
style={styles.input}
onChangeText={(amount) => this.setState({ amount })}
value={this.state.amount}
placeholder="Amount"
keyboardType="numeric"
/>
{this.state.amount === '' && (
<Text style={{ color: 'red', fontSize: 12, paddingLeft: 12 }}>
Amount is required
</Text>
)}
<Button
title="Add"
style={styles.btn}
onPress={() => {
if (
this.state.name.trim() === '' ||
this.state.amount.trim() === ''
) {
alert('Please Enter the required values.');
} else {
this.submitExpense(
this.state.name,
this.state.amount
);
}
}}
/>
<Button
title="Go to Dashboard"
style={styles.btn}
onPress = {() => { this.props.navigation.navigate("Home", {
expenseList: this.state.expenseList,
});}}
/>
<FlatList
style={styles.listContainer}
data={this.state.expenseList}
renderItem={(data) => <Text> title={data.item.name} </Text>}
/>
</View>
);
}
}

You have a few options:
1- Use Redux:
https://react-redux.js.org/using-react-redux/connect-mapstate
2- Save your state in the AsyncStorage and get it wherever you want
3- Pass your state as param in the route:
navigation.navigate('Details', {
itemId: 86,
otherParam: 'anything you want here',
});

Related

Flatlist doesn't display the list from useContext

In react native app, I have a home screen and a second screen that the user uses to add items that should be displayed on the home screen. I am using context to save the list of items. The problem is when I add items to the second screen and go to the home screen. The displayed list is empty.
Any help to explain why this happens and how to handle it? Here's the
Data Context
export const ExpenseContext = createContext();
App.js
const Stack = createNativeStackNavigator();
function App() {
const [expenseList, setExpenseList] = useState([]);
return (
<NavigationContainer>
<ExpenseContext.Provider value={{ expenseList, setExpenseList }}>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options={{ title: "Dashboard" }}
/>
<Stack.Screen
name="AddItem"
component={AddItem}
options={{ title: "CashFlowCreate" }}
/>
</Stack.Navigator>
</ExpenseContext.Provider>
</NavigationContainer>
);
}
export default App;
Home.js
function Home({ route, navigation }) {
const { expenseList } = useContext(ExpenseContext);
return (
<View style={styles.container}>
<Text style={styles.text}>Budget:</Text>
<Button title="+" onPress={() => navigation.navigate("AddItem")} />
<View>
<FlatList
style={styles.listContainer}
data={expenseList}
renderItem={(data) => <Text>{data.item.name}</Text>}
/>
</View>
</View>
);
}
export default Home;
AddItem.js
function AddItem({ navigation }) {
const { expenseList, setExpenseList } = useContext(ExpenseContext);
const [name, setName] = useState("");
const [amount, setAmount] = useState("");
const itemsList = expenseList;
return (
<View style={styles.container}>
<TextInput
style={styles.input}
onChangeText={(name) => setName( name )}
value={name}
placeholder="Name"
keyboardType="default"
/>
{name === "" && (
<Text style={{ color: "red", fontSize: 12, paddingLeft: 12 }}>
Name is required
</Text>
)}
<TextInput
style={styles.input}
onChangeText={(amount) => setAmount( amount )}
value={amount}
placeholder="Amount"
keyboardType="numeric"
/>
{amount === "" && (
<Text style={{ color: "red", fontSize: 12, paddingLeft: 12 }}>
Amount is required
</Text>
)}
<Button
title="Add"
style={styles.btn}
onPress={() => {
if (name === "" || amount === "") {
alert("Please Enter the required values.");
} else {
itemsList.push({
name: name,
amount: amount,
});
setExpenseList(itemsList);
}
}}
/>
<Button
title="View Dashboard"
style={styles.btn}
onPress={() => {
navigation.navigate("Home");
}}
/>
</View>
);
}
export default AddItem;
I solve it, in AddItem component remove const itemsList = expenseList; and onPress add button it should be like that instead
onPress={() => {
name === "" || amount === ""
? alert("Please Enter the required values.")
: setExpenseList([
...expenseList,
{
key:
Date.now().toString(36) +
Math.random().toString(36).substr(2),
name: name,
amount: amount,
},
]);
}}
I added the key because I needed later on.
There are several areas of issues in your code. One issue I can see is in AddItem. When you set:
const itemsList = expenseList
I think you did this for:
itemsList.push({
name: name,
amount: amount,
});
But you should look at the spread operator and try:
setExpenseList(...expenseList, {
name,
amount,
})
rewrite of AddItem.js:
function AddItem({ navigation }) {
const { expenseList, setExpenseList } = useContext(ExpenseContext)
const [name, setName] = useState('')
const [amount, setAmount] = useState('')
return (
<View style={styles.container}>
<TextInput style={styles.input} onChangeText={setName} value={name} placeholder='Name' keyboardType='default' />
{name === '' ? <Text style={styles.err}>Name is required</Text> : null}
<TextInput style={styles.input} onChangeText={setAmount} value={amount} placeholder='Amount' keyboardType='numeric' />
{amount === '' ? <Text style={styles.err}>Amount is required</Text> : null}
<Button
title='Add'
style={styles.btn}
onPress={() => {
name === '' || amount === ''
? alert('Please Enter the required values.')
: setExpenseList(...expenseList, {
name: name,
amount: amount,
})
}}
/>
<Button title='View Dashboard' style={styles.btn} onPress={() => navigation.navigate('Home')} />
</View>
)
}
export default AddItem
In your Home.js your FlatList it's missing the keyExtractor and you're trying to declare a prop of title outside of <Text>, rewrite:
function Home({ navigation }) {
const { expenseList } = useContext(ExpenseContext);
return (
<View style={styles.container}>
<Text style={styles.text}>Budget:</Text>
<Button title="+" onPress={() => navigation.navigate("AddItem")} />
<View>
<FlatList
style={styles.listContainer}
data={expenseList}
keyExtractor={(_,key) => key.toString()}
renderItem={(data) => <Text>{data.item.name}</Text>}
/>
</View>
</View>
);
}
export default Home;
Edit:
Answering to the comment. My understanding of the docs that is incorrect because keyExtractor is for identifying the id and by your commented code unless your passed in data to FlatList has a property of key then that wont work.
Also if key is not a string it should be:
keyExtractor={(item) => item.key.toString()}

React-Native modal not showing FlatList Items

Modal only displays button but not FlatList item.
I am trying to build a custom app picker for my react-native-app which renders a list of items on a modal.
AppPicker.js
const AppPicker = ({ icon, items, placeholder }) => {
const [modalVisible, setModalVisible] = React.useState(false)
return (
<>
<TouchableWithoutFeedback onPress={() => setModalVisible(true)} >
<View style={styles.container}>
{icon && <MaterialCommunityIcons name={icon} size={20} style={styles.icon} />}
<AppText style={styles.text}> {placeholder} </AppText>
<MaterialCommunityIcons name="chevron-down" size={20} />
</View>
</TouchableWithoutFeedback>
<Modal visible={modalVisible} animationType="slide" >
<Button title="Close" onPress={() => setModalVisible(false)} />
<FlatList
data={items}
keyExtractor={item => item.value.toString()}
renderItem={({ item }) =>
<PickerItem
lable={item.label}
onPress={() => console.log(item)}
/>}
/>
</Modal>
</>
)
}
the <PickerItem/> that should be rendered by the flatlist is a component that just displays the items from the list.
PickerItem.js
const PickerItem = ({ label, onPress }) => {
return (
<TouchableOpacity onPress={onPress}>
<AppText> {label} </AppText>
</TouchableOpacity>
)
}
export default PickerItem;
when I randomly tap on the modal screen however the onPress() on the FlatList triggers the console.log(item) and the items are displayed on the console. Any help on this will be appreciated.
** App.js **
const categories = [
{ label: "furniture", value: 1 },
{ label: "clothing", value: 2 },
{ label: "phone", value: 3 },
]
export default function App() {
return (
<Screen style={styles.container}>
<AppPicker items={categories} icon="apps" placeholder="category" />
<AppTextInput icon="email" placeholder="email" />
</Screen>
);
}
NOTE: I am using expo to run the application.

Retrieve a list of products and display them

I am trying to retrieve a list of products from my API. For the moment, nothing is displayed, the page is currently empty except for my back button and I do not understand why. The two functions I use are functional since both console.log works. Could you help me ?
Everything looks fine, the console.log work in the terminal but I can't display anything in the app.
I tried this snack : https://snack.expo.io/O4oPj8-Qz
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.productItem, style]}>
<Text style={[styles.h4, {textAlign: "left"}]}>
{item.name}
</Text>
</TouchableOpacity>
);
export default class Products extends Component {
constructor(props) {
super(props);
this.state = {
selectedId: '',
setSelectedId: '',
listData: '',
currentPage: 1,
loadMoreVisible: true,
loadMoreVisibleAtEnd: false,
displayArray: null
}
};
initListData = async () => {
let list = await getProducts(1);
console.log(list)
if (list) {
this.setState({
displayArray: list,
loadMoreVisible: (list.length >= 15 ? true : false),
currentPage: 2
});
}
};
setNewData = async (page) => {
let list = await getProducts(parseInt(page));
if (list) {
this.setState({
displayArray: this.state.displayArray.concat(list),
loadMoreVisible: (list.length >= 15 ? true : false),
loadMoreVisibleAtEnd: false,
currentPage: parseInt(page)+1
});
}
};
loadMore() {
this.setNewData(this.state.currentPage);
}
displayBtnLoadMore() {
this.setState({
loadMoreVisibleAtEnd: true
});
}
async UNSAFE_componentWillMount() {
this.initListData();
}
render() {
return (
<View>
{this.state.displayArray !== null && this.state.displayArray.length > 0 ? (
<View style={{ flex: 1, marginBottom: 100 }}>
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.displayArray}
extraData={this.selectedId}
onEndReached={() => this.displayBtnLoadMore()}
renderItem={({item})=>
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('ProductDetails', {productId: parseInt(item.id)})}
/>
</View>
}
keyExtractor={item => "product-" + item.id.toString()}
style={{width:"90%"}}
/>
{this.state.loadMoreVisible === true && this.state.loadMoreVisibleAtEnd === true ? (
<Button title=" + " onPress={()=>{this.loadMore()}}></Button>
) : null
}
<View style={styles.container}>
<Text>{"\n"}</Text>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
back
</Text>
</View>
</TouchableOpacity>
</View>
<Text>{"\n\n"}</Text>
</SafeAreaView>
</View>
) : (
<View style={styles.container}>
<Text>{"\n\n" + (this.state.displayArray === null ? i18n.t("products.searching") : i18n.t("products.nodata")) + "\n\n\n"}</Text>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
Back
</Text>
</View>
</TouchableOpacity>
</View>
)}
</View>
);
};
}
You were not adding flex: 1 and also not calling the API in the right way, here is the snack with the solution.
Link to snack
Thanks to your answers and help. I got it work this way : Thank you so much for yout time and help, really, sincerely.
import React, { Component } from "react";
import { FlatList, SafeAreaView, Button, Text, View, TouchableOpacity } from 'react-native';
import { getProducts } from '../../../src/common/Preferences';
import styles from '../../../assets/styles';
import i18n from '../../../src/i18n';
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.productItem, style]}>
<Text style={[styles.h4, {textAlign: "left"}]}>
{item.name}
</Text>
</TouchableOpacity>
);
export default class Products extends Component {
constructor(props) {
super(props);
this.state = {
selectedId: '',
setSelectedId: '',
listData: '',
currentPage: 1,
loadMoreVisible: true,
loadMoreVisibleAtEnd: false,
displayArray: []
}
};
initListData = async () => {
let list = await getProducts(1);
if (list) {
this.setState({
displayArray: list,
loadMoreVisible: (list.length >= 15 ? true : false),
currentPage: 2
});
console.log(this.state.displayArray, 'dans initListData')
}
};
setNewData = async (page) => {
let list = await getProducts(parseInt(page));
if (list) {
this.setState({
displayArray: this.state.displayArray.concat(list),
loadMoreVisible: (list.length >= 15 ? true : false),
loadMoreVisibleAtEnd: false,
currentPage: parseInt(page)+1
});
}
};
loadMore() {
this.setNewData(this.state.currentPage);
}
displayBtnLoadMore() {
this.setState({
loadMoreVisibleAtEnd: true
});
}
async UNSAFE_componentWillMount() {
this.initListData();
console.log(this.state.displayArray, 'dans componentWillMount')
}
render() {
console.log('displayArray', this.state.displayArray)
return (
<View style={{flex: 1}}>
<Text>{"\n"}</Text>
<Text>{"\n"}</Text>
{this.state.displayArray !== null && this.state.displayArray.length > 0 ? (
<View style={{ flex: 1, marginBottom: 100 }}>
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.displayArray}
//extraData={this.selectedId}
//onEndReached={() => this.displayBtnLoadMore()}
renderItem={({item})=>
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('ProductDetails', {productId: parseInt(item.id)})}
/>
</View>
}
keyExtractor={item => "product-" + item.id.toString()}
style={{width:"90%"}}
/>
{this.state.loadMoreVisible === true && this.state.loadMoreVisibleAtEnd === true ? (
<Button title=" + " onPress={()=>{this.loadMore()}}></Button>
) : null
}
<View style={styles.container}>
<Text>{"\n"}</Text>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.container}>
<Button
color="#F78400"
title= 'Back'
onPress={() => this.props.navigation.goBack()}>BACK
</Button>
</View>
</TouchableOpacity>
</View>
<Text>{"\n\n"}</Text>
</SafeAreaView>
</View>
) : (
<View style={styles.container}>
<Text>{"\n\n" + (this.state.displayArray === null ? i18n.t("products.searching") : i18n.t("products.nodata")) + "\n\n\n"}</Text>
<Button
color="#F78400"
title= 'Back'
onPress={() => this.props.navigation.goBack()}>BACK
</Button>
</View>
)}
</View>
);
};
}

Navigating back to start screen

I have two different documents with two different screens. After an e-mail validation on the first screen, I'm now able to go to the second screen. However, I want to return to the first screen. None of the mentioned approaches on the React Navigation 5.x documentation works for me.
This is the code on the App.js:
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import firebase from './firebase';
import * as EmailValidator from 'email-validator';
import { HitTestResultTypes } from 'expo/build/AR';
import logo from './assets/Circulo.png';
import AgeInput from './AgeInput';
// Clase que representa el diseño de la pantalla inicial de la app
class HomeScreen extends Component {
state = { username: null, password: null, nonValidInput: null }
_onSubmit = ({ navigation }) =>{
if(EmailValidator.validate(this.state.username) == true) {
this.setState({ nonValidInput: false });
const { username, password } = this.state;
try {
// THIS IS WHERE I GO TO THE SECOND SCREEN
firebase.auth().signInWithEmailAndPassword(this.state.username, this.state.password).then(() => this.props.navigation.navigate('Age'));
} catch {
Alert.alert(
'Error',
'Los datos no son correctos',
[
{ text: 'Ok' }
],
{ cancelable: false }
);
}
} else {
this.setState({ nonValidInput: true });
}
}
render() {
return (
<KeyboardAwareScrollView contentContainerStyle={styles.container} scrollEnabled
enableOnAndroid={true} resetScrollToCoords={{x:0, y:0}}>
<View style={styles.logo}>
<Image source = {logo} style={styles.img}/>
<Text style={styles.textLogoPrimary}>Neuron App</Text>
<Text style={styles.textLogoSecondary}>Test</Text>
</View>
<View style={styles.formElement}>
<Text style={styles.formText}>Correo Electrónico</Text>
<TextInput keyboardType='email-address' placeholder='Email' onChangeText={value => this.setState({ username: value })}
style={styles.formInput} />
{this.state.nonValidInput ? (
<Text style={styles.textAlert}>Correo electrónico no valido.</Text>
) : null}
</View>
<View style={styles.formElement}>
<Text style={styles.formText}>Contraseña</Text>
<TextInput style={styles.formInput} placeholder='Contraseña' onChangeText={value => this.setState({ password: value })}
secureTextEntry={true}/>
</View>
<View style={styles.buttonView}>
<TouchableOpacity style={styles.button} onPress={this._onSubmit}>
<Text style={styles.buttonText}>Iniciar</Text>
</TouchableOpacity>
</View>
</KeyboardAwareScrollView>
);
}
}
const Stack = createStackNavigator();
class App extends Component {
render() {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false}} initialRouteName="Home">
<Stack.Screen name='Home' component={HomeScreen} />
<Stack.Screen name='Age' component={AgeInput} />
</Stack.Navigator>
</NavigationContainer>
);
}
}
and this is the code on the AgeInput.js
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import Home from './App';
class App extends Component { // AgeInput
state = { date: null, edad: null, day: null, month: null, year: null }
_ageCalc = () => {
if(this.state.day < 32 && this.state.day > 0 && this.state.month < 13 && this.state.month > 0 && this.state.year != 0) {
var fecha = Date.parse(this.state.year + '-' + this.state.month + '-' + this.state.day);
var hoy = new Date();
var fechaNacimiento = new Date(fecha);
var edad_ahora = hoy.getFullYear() - fechaNacimiento.getFullYear();
var mes = hoy.getMonth() - fechaNacimiento.getMonth();
if (mes < 0 || (mes === 0 && hoy.getDate() < fechaNacimiento.getDate())) {
edad_ahora--;
}
this.setState({ edad: edad_ahora });
} else {
Alert.alert(
'Error',
'Por favor introduce una fecha valida',
[
{ text: 'Ok' }
],
{ cancelable: false },
);
}
}
render () {
return (
<KeyboardAwareScrollView contentContainerStyle={styles.container} scrollEnabled enableOnAndroid={true}
resetScrollToCoords={{x:0, y:0}}>
<View style={styles.topView}>
// This is the button I press to go back to the first screen
<TouchableOpacity style={styles.img} onPress={() => this.props.navigator.navigate('Home')}>
<Image source={flecha} />
</TouchableOpacity>
<View style={styles.topTextWrapper}>
<Text style={styles.topTextPrimary}>Bienvenido a Neuron</Text>
<Text style={styles.topTextSecondary}>¿O no?</Text>
</View>
</View>
<View style={styles.middleView}>
<Text style={styles.formText}>Fecha de nacimiento</Text>
<View style={styles.formRow}>
<View style={styles.textInputWrapper}>
<TextInput style={styles.formInput} placeholder='DD' keyboardType='number-pad'
onChangeText={ value => this.setState({ day: value }) }/>
</View>
<View style={styles.textInputWrapper}>
<TextInput style={styles.formInput} placeholder='MM' keyboardType='number-pad'
onChangeText={ value => this.setState({ month: value }) }/>
</View>
<View style={styles.textInputWrapper}>
<TextInput style={styles.formInput} placeholder='AA' keyboardType='number-pad'
onChangeText={ value => this.setState({ year: value }) }/>
</View>
</View>
</View>
<View style={styles.buttonView}>
<TouchableOpacity style={styles.button} onPress={this._ageCalc}>
<Text style={styles.buttonText}>CALCULAR EDAD</Text>
</TouchableOpacity>
</View>
<View style={styles.ageView}>
<Text style={styles.ageTextPrimary}>Tu edad es:</Text>
<Text style={styles.ageNumber}>{this.state.edad}</Text>
<Text style={styles.ageTextSecondary}>Años</Text>
</View>
</KeyboardAwareScrollView>
);
}
}
export default App; // AgeInput
Thanks for your help
You can do something like this...
render () {
const { navigate } = props.navigation;
//function to go to next screen
goToNextScreen = () => {
return navigate('Home');
return (
<KeyboardAwareScrollView contentContainerStyle={styles.container} scrollEnabled enableOnAndroid={true}
resetScrollToCoords={{x:0, y:0}}>
<View style={styles.topView}>
// This is the button I press to go back to the first screen
<TouchableOpacity style={styles.img} onPress={() => this.goToNextScreen()}>
<Image source={flecha} />
</TouchableOpacity>
<View style={styles.topTextWrapper}>
<Text style={styles.topTextPrimary}>Bienvenido a Neuron</Text>
<Text style={styles.topTextSecondary}>¿O no?</Text>
</View>
</View>
<View style={styles.middleView}>
<Text style={styles.formText}>Fecha de nacimiento</Text>
<View style={styles.formRow}>
<View style={styles.textInputWrapper}>
<TextInput style={styles.formInput} placeholder='DD' keyboardType='number-pad'
onChangeText={ value => this.setState({ day: value }) }/>
</View>
<View style={styles.textInputWrapper}>
<TextInput style={styles.formInput} placeholder='MM' keyboardType='number-pad'
onChangeText={ value => this.setState({ month: value }) }/>
</View>
<View style={styles.textInputWrapper}>
<TextInput style={styles.formInput} placeholder='AA' keyboardType='number-pad'
onChangeText={ value => this.setState({ year: value }) }/>
</View>
</View>
</View>
<View style={styles.buttonView}>
<TouchableOpacity style={styles.button} onPress={this._ageCalc}>
<Text style={styles.buttonText}>CALCULAR EDAD</Text>
</TouchableOpacity>
</View>
<View style={styles.ageView}>
<Text style={styles.ageTextPrimary}>Tu edad es:</Text>
<Text style={styles.ageNumber}>{this.state.edad}</Text>
<Text style={styles.ageTextSecondary}>Años</Text>
</View>
</KeyboardAwareScrollView>
);
}
}
export default App;
Just replace this :
// This is the button I press to go back to the first screen
<TouchableOpacity style={styles.img} onPress={() => this.props.navigator.navigate('Home')}>
with
// This is the button I press to go back to the first screen
<TouchableOpacity style={styles.img} onPress={() => this.props.navigation.navigate('Home')}>
Hope it help.s

Not showing data fetched from api ( React Native )

I wanted to show a list of data fetched from API inside DropdownModal (https://github.com/sohobloo/react-native-modal-dropdown) . The data is user address consists of name , state , country and all related to address . But it won't show inside the dropdown and it shows loading icon which means it is null or undefined . But i did have the data fetched from the API which i verify by making alert to error and result ( yup both giving the same data which is the address ) .
Below are my code .
const {getAddresses} = auth;
var {width, height} = Dimensions.get('window');
class RegisterEventOne extends React.Component {
constructor(props) {
super(props);
this.state = {
event_id: '',
tshirt_size: '',
size: '',
address: '',
addressx: '',
};
this.onResult = this.onResult.bind(this);
this.onError = this.onError.bind(this);
}
handleWithDropdownCategory = id => {
this.setState({event_id: id});
};
handleWithDropdownSize = size => {
this.setState({tshirt_size: size});
};
TShirtSize = size => {
this.setState({size: size});
};
setAddress = address => {
this.setState({addressx: address})
}
componentDidMount() {
this.props.getAddresses(this.props.event.id, this.onResult, this.onError);
}
onError(error) {
alert(JSON.stringify(error));
}
onResult(result) {
this.setState({
address: result,
});
}
render() {
return (
<React.Fragment>
<StatusBar backgroundColor="black" barStyle="light-content" />
<SafeAreaView style={styles.container}>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={styles.scrollView}>
<View>
<Text style={styles.eventname}>{this.props.event.name}</Text>
<ModalDropdown
dropdownStyle={styles.dropdown}
dropdownTextStyle={{fontSize:15}}
style={styles.dropdown}
onSelect={(index, value) => {
this.handleWithDropdownCategory(value);
}}
options={this.props.event.categories.map(function(event) {
return event.name;
})}>
<View style={styles.dropdowncontainer}>
<Text>{this.state.event_id === '' ? 'Select Category' : this.state.event_id}</Text>
<Ionicons name="ios-arrow-down" size={20} color="black" />
</View>
</ModalDropdown>
<ModalDropdown
dropdownStyle={styles.dropdown}
dropdownTextStyle={{fontSize:15}}
style={styles.dropdown}
onSelect={(index, value) => {
this.handleWithDropdownSize(value);
this.TShirtSize(index+1);
}}
options={this.props.event.tshirts.map(function(event, index) {
return event.size;
})}
>
<View style={styles.dropdowncontainer}>
<Text>{this.state.tshirt_size === '' ? 'Select Tshirt Size' : this.state.tshirt_size}</Text>
<Ionicons name="ios-arrow-down" size={20} color="black" />
</View>
</ModalDropdown>
<ModalDropdown
dropdownStyle={styles.dropdown}
style={styles.dropdown}
dropdownTextStyle={{fontSize:15}}
onSelect={(index, value) => {
this.setAddress(value);
}}
options={this.state.address !== '' ? this.state.address.map(function(address, index) {
return address.id;
}):null}
>
<View style={styles.dropdowncontainer}>
<Text>{this.state.addressx === '' ? 'Select Address' : this.state.addressx}</Text>
<Ionicons name="ios-arrow-down" size={20} color="black" />
</View>
</ModalDropdown>
{/* <Text style={styles.header}>Compete with ohters (Optional)</Text>
<TextInput
style={styles.header}
onChangeText={text => onChangeText(text)}
placeholder="Set Date & Time (Time zone)"
/> */}
{/* <View style={styles.checkboxcontainer}>
<BouncyCheckbox
textColor="#000"
fillColor="orange"
fontFamily="JosefinSans-Regular"
text="Individual Competition"
/>
<BouncyCheckbox
textColor="#000"
fillColor="orange"
fontFamily="JosefinSans-Regular"
text="Team Competition"
/>
<TextInput
style={styles.header}
onChangeText={text => onChangeText(text)}
placeholder="Team member limit"
/>
<TextInput
style={styles.header}
onChangeText={text => onChangeText(text)}
placeholder="Username / Email"
/>
<TextInput
style={styles.header}
onChangeText={text => onChangeText(text)}
placeholder="Username / Email"
/>
<TextInput
style={styles.header}
onChangeText={text => onChangeText(text)}
placeholder="Username / Email"
/>
</View> */}
</View>
</ScrollView>
<View style={styles.processIndicator}>
<TouchableOpacity disabled>
<Text style={styles.textProcessPrimary}>Previous</Text>
</TouchableOpacity>
<TouchableOpacity onPress={()=>Actions.RegisterEventThree({event_id: this.props.event.categories[0].event_id, category_id: this.state.event_id, size: this.state.size, address: this.state.addressx})}>
<Text style={styles.textProcessPrimary}>Next</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
</React.Fragment>
);
}
}
export default connect(
null,
{getAddresses},
)(RegisterEventOne);
The API :
export function getAddresses(data, callback) {
AsyncStorage.getItem('token').then(value => {
const token = JSON.parse(value);
fetch('https:apiurl.com', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'bearer' + token.access_token,
},
})
.then(response => response.json())
.then(response => callback(response.data))
.catch(error => callback(false, null, error.json()));
The loading indicator shows only if data(options) is undefined or null. Which means that you have no data at all, or data structure is bad.
You'v said that error alert is also triggered, which is not really a great thing. I don't know why the error is showing you some data tho. (except of error data).
Options should be passed in this format: ['data1', 'data2'].
Also, your taking the data from redux => this.props.event.categories instead of state. If you want to use redux, then you are missing some kind of mapStateToProps in connect fnc.
There is a lot of wrong patterns in this code. take a look at some examples of how to use redux and also take a look at examples in react-native-modal-dropdown github repo if you want to use that.
It's solved now .
I just added ,true,null behind response.data .
It would look like this :
.then(response => callback(response.data,true,null)