Flatlist doesn't display the list from useContext - 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. 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()}

Related

how to submit a form in react native and submit to a post api

I want to save the values from all input fields to getdata(), but I am getting undefined value
export default function Signupfor(props) {
// const phoneInput = useRef < PhoneInput > null;
const [text, setTextname] = useState();
function getdata() {
console.log('dsd');
console.log(text);
}
const {userInfo, log} = props?.route?.params;
console.log(log.name);
return (
<View style={styles.prheight}>
<View style={styles.form}>
<Text style={styles.r}>One Last Step</Text>
<TextInput
style={styles.forminput}
label="Name"
value={userInfo.user.name}
onChangeText={text => setTextname(text)}
/>
<TextInput
style={styles.forminput}
label="Email"
value={userInfo.user.email}
onChangeText={text => setTextemail(text)}
/>
<TextInput
style={styles.forminput}
label="Whatsapp Number"
keyboardType="numeric"
value={userInfo.user.number}
onChangeText={text => setTextnumber(text)}
// value={this.state.myNumber}
maxLength={10} //setting limit of input
/>
</View>
<View style={styles.buttonw}>
<Button color="#7743DB" title="Lets Go" onPress={() => getdata()} />
</View>
</View>
);
}
Here, name and email should not be able to be edited. I want to pass the value from value={userInfo.user.name} to the getdata()
you can use formik package for making form in react native
Installation
yarn add formik
Usage
import { Formik } from "formik";
export default function Signupfor(props) {
const { userInfo, log } = props?.route?.params;
console.log(log.name);
return (
<Formik
initialValues={{
name: userInfo.user.name,
email: userInfo.user.email,
number: userInfo.user.number,
}}
onSubmit={async (values, actions) => {
try {
console.log("name", values.name);
console.log("phone", values.number);
const params = {
full_name: values.name,
email: values.email,
phone_number: values.number,
};
} catch (error) {
let message = error.message;
console.log(message)
} finally {
actions.setSubmitting(false);
}
}}
>
{({
handleChange,
setFieldValue,
handleSubmit,
values,
errors,
touched,
isSubmitting,
}) => (
<View style={styles.prheight}>
<View style={styles.form}>
<Text style={styles.r}>One Last Step</Text>
<TextInput
style={styles.forminput}
label="Name"
value={values.name}
onChangeText={handleChange("name")}
/>
<TextInput
style={styles.forminput}
label="Email"
value={values.email}
onChangeText={handleChange("email")}
/>
<TextInput
style={styles.forminput}
label="Whatsapp Number"
keyboardType="numeric"
value={values.number}
onChangeText={handleChange("number")}
// value={this.state.myNumber}
maxLength={10} //setting limit of input
/>
</View>
<View style={styles.buttonw}>
<Button
color="#7743DB"
title="Lets Go"
onPress={() => handleSubmit()}
/>
</View>
</View>
)}
</Formik>
);
}
Your original method doesn't populate the state unless you edit the text input field, this is because your initialState doesn't have a value to start with. so firing getData() is reading empty state if the fields havent been changed.
onChangeText={text => setTextname(text)}
Only fire if you edit the textInput field.
Also I think you might be missing props, so first check if you are getting the correct data from props by logging it.
Once you have confirmed the props are available.
Set the initialState for name to userInfo.user.name
const { userInfo } = props?.route?.params;
const [name, setName] = useState(userInfo.user.name);
Then pass the state name to your TextInput and it should populate the value by reading from state.
return (
<>
<TextInput
placeholder="name"
value={name}
onChangeText={(text) => setName(text)}
/>
<Button title="Submit" onPress={() => getData()} />
</>
)
Make sure to create states for any additional values you wish to save.
const [name, setName] = useState(userInfo.user.name);
const [email, setEmail] = useState(userInfo.user.email);
You can use a library like https://react-hook-form.com to check an example with react native on video.
Or you can right it yourself, in the example below any time you need access to input values you can read it from text and number
const UselessTextInput = () => {
const [text, onChangeText] = useState("Useless Text");
const [number, onChangeNumber] = useState(null);
return (
<SafeAreaView>
<TextInput
style={styles.input}
onChangeText={onChangeText}
value={text}
/>
<TextInput
style={styles.input}
onChangeText={onChangeNumber}
value={number}
placeholder="useless placeholder"
keyboardType="numeric"
/>
</SafeAreaView>
);
};
You can do something like this!!
export default function Signupfor(props) {
const {userInfo, log} = props?.route?.params;
const [name, setName] = useState(userInfo?.user?.name);
const [phone, setPhone] = useState(userInfo?.user?.number);
function getdata() {
console.log("name",name)
console.log("phone",phone)
}
return (
<View style={styles.prheight}>
<View style={styles.form}>
<Text style={styles.r}>One Last Step</Text>
<TextInput
style={styles.forminput}
label="Name"
// this value must be same with useState
value={name}
onChangeText={text => setName(text)}
/>
<TextInput
style={styles.forminput}
label="Mobile"
value={phone}
onChangeText={text => setPhone(text)}
/>
</View>
<View style={styles.buttonw}>
<Button color="#7743DB" title="Lets Go" onPress={() => getdata()} />
</View>
</View>
);
}
Same goes for email.

initialState resets after dispatch actions in react useReducer

In my app, I fetch users data from the server inside the useEffect hook and set the initialState of the useReducer. When action dispatch happens on text input change, the initialState resets instead of updating thus I can't type a word inside the input. Can someone figure out the problem, please? I'm fairly new to react reducers. I have attached the relevant codes. Thanks.
EditProfile.js
const EditProfile = ({ navigation, route }) => {
const userid = route.params.userid;
const { user } = React.useContext(AuthContext);
const [userData, setUserData] = React.useState(null);
const initialState = {
name: userData ? (userData.name ? userData.name : "") : "",
dob: userData ? (userData.dob ? userData.dob.toDate() : "") : "",
phone: userData ? (userData.phone ? userData.phone : "") : "",
location: userData ? (userData.location ? userData.location : "") : "",
caption: userData ? (userData.caption ? userData.caption : "") : "",
};
React.useEffect(() => {
async function fetchData() {
const response = await getUserData(userid);
setUserData(response);
}
fetchData();
}, []);
const reducer = (state, action) => {
switch (action.type) {
case "nameInputChange":
return {
...state,
name: action.name,
};
case "dobInputChange":
return {
...state,
dob: action.dob,
};
case "phoneInputChange":
return {
...state,
phone: action.phone,
};
case "locationInputChange":
return {
...state,
location: action.location,
};
case "captionInputChange":
return {
...state,
caption: action.caption,
};
}
};
const [data, dispatch] = React.useReducer(reducer, initialState);
const nameInputChange = (value) => {
dispatch({
type: "nameInputChange",
name: value,
});
console.log("Name: ", initialState.name);
};
const dobInputChange = (date) => {
dispatch({
type: "dobInputChange",
dob: date,
});
};
const phoneInputChange = (value) => {
dispatch({
type: "phoneInputChange",
phone: value,
});
};
const locationInputChange = (value) => {
dispatch({
type: "locationInputChange",
location: value,
});
};
const captionInputChange = (value) => {
dispatch({
type: "captionInputChange",
caption: value,
});
};
return (
<View>
<FlatList
showsVerticalScrollIndicator={false}
ListHeaderComponent={() => (
<View>
<TouchableOpacity>
<Image
source={require("../assets/images/profile.jpg")}
style={{ width: "98%", height: "98%", borderRadius: 59 }}
/>
<View>
<Entypo name="camera" color="#8000e3" size={18} />
</View>
</TouchableOpacity>
<View>
<VerticalNameInput
type="name"
label="Full Name"
placeholder="Full name"
color="#9798ac"
placeholder="enter your full name"
onChangeText={(value) => nameInputChange(value)}
value={initialState.name}
/>
<VerticalDateInput
label="Date of Birth"
color="#9798ac"
dobInputChange={dobInputChange}
initialState={initialState}
/>
</View>
<View>
<VerticalNameInput
type="mobile"
label="Mobile"
color="#9798ac"
placeholder="mobile number"
onChangeText={(value) => phoneInputChange(value)}
value={initialState.phone}
/>
<VerticalNameInput
type="location"
label="Location"
color="#9798ac"
placeholder="city and country"
onChangeText={(value) => locationInputChange(value)}
value={initialState.location}
/>
</View>
<View>
<VerticalNameInput
type="caption"
label="Profile Caption"
color="#9798ac"
placeholder="enter about yourself"
onChangeText={(value) => captionInputChange(value)}
value={initialState.caption}
/>
</View>
</View>
)}
/>
<View style={{ position: "relative", top: -90, left: 200 }}>
<FloatingAction
onPressMain={() => {
updateUser(userid, userData);
}}
floatingIcon={<Entypo name="check" size={28} color="#fff" />}
/>
</View>
</View>
);
};
export default EditProfile;
VerticalNameInput.js
const VerticalNameInput = ({ label, color, placeholder, type, ...rest }) => {
return (
<View>
<Text>
{label}
</Text>
<View>
<View>
{type === "name" ? (
<AntDesign name="user" color="#000" size={15} />
) : type === "mobile" ? (
<AntDesign name="mobile1" color="#000" size={15} />
) : type === "location" ? (
<EvilIcons name="location" color="#000" size={20} />
) : type === "caption" ? (
<Ionicons
name="information-circle-outline"
color="#000"
size={18}
/>
) : null}
</View>
<TextInput
style={{ width: "85%", height: "100%" }}
numberOfLines={1}
placeholder={placeholder}
placeholderTextColor={color}
{...rest}
/>
</View>
</View>
);
};
export default VerticalNameInput;
VerticalDateInput.js
const VerticalDateInput = ({ label, color, dobInputChange, initialState }) => {
const [date, setDate] = React.useState(new Date());
const [open, setOpen] = React.useState(false);
return (
<View>
<Text>
{label}
</Text>
<View>
<View>
<AntDesign name="calendar" color="#000" size={15} />
</View>
<View>
<Text style={{ marginLeft: 10 }}>
{initialState.dob
? initialState.dob.toDateString()
: new Date().toDateString()}
</Text>
<TouchableOpacity
style={{ marginRight: 10 }}
onPress={() => setOpen(true)}
>
<AntDesign name="caretdown" color="#000" size={12} />
</TouchableOpacity>
</View>
<DatePicker
maximumDate={new Date()}
mode="date"
modal
open={open}
date={initialState.dob ? initialState.dob : date}
onConfirm={(date) => {
setOpen(false);
setDate(date);
dobInputChange(date);
}}
onCancel={() => {
setOpen(false);
}}
/>
</View>
</View>
);
};
export default VerticalDateInput;
Try add "default" case return current state in your reducer.
It might happen that you dispatch some unknown action, and reducer return undefined as a result.
const reducer = (state = initialState, action) => {
switch (action.type) {
default:
// If this reducer doesn't recognize the action type, or doesn't
// care about this specific action, return the existing state unchanged
return state
}
}

How to prevent state reset while navigating between screens? 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',
});

Error: Element type is invalid when creating Input in Component

I have created a sub component for updating values of recipes I fetch from API. However when I do so I get:
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of `UpdateCourse`.
Navigator to the Update function component:
<Button
title="UPDATE RECIPE"
style={Styles.searchBtn}
onPress={() =>
navigation.navigate("Update_course", {
id: "60c9a32cb076563888b5782a",
})
}
/>
</View>
Update Component:
import React, { useState, useEffect } from "react";
import { Button, Image, TextInput, Text, Linking, View } from "react-native";
import Styles from "../../styles/Styles";
import Alert from "../elements/Alert";
import Axios from "axios";
import APIRequest from "../elements/APIRequest";
export default function UpdateCourse(id) {
const [dishName, setdishName] = useState("");
const [category, setcategory] = useState("");
const [author, setauthor] = useState("");
const [ingredients, setingredients] = useState([]);
const [cookingTime, setcookingTime] = useState("");
const [sourceUrl, setsourceUrl] = useState("");
const [imageUrl, setimageUrl] = useState("");
const [isPublished, setisPublished] = useState("true");
const [price, setprice] = useState("");
const [tags, settags] = useState([]);
const [alert, setAlert] = useState("");
const url = `http://x.x.x.x:1234/api/courses/find/${id}`;
useEffect(() => {
async function getData() {
const result = await Axios.get(url);
setdishName(result.data.dishName);
setcategory(result.data.category);
setauthor(result.data.author);
setingredients(result.data.ingredients);
setcookingTime(result.data.cookingTime);
setsourceUrl(result.data.sourceUrl);
setimageUrl(result.data.imageUrl);
setisPublished(result.data.isPublished);
setprice(result.data.price);
settags(result.data.tags);
}
getData();
}, [url]);
async function update() {
let item = {
dishName,
category,
author,
ingredients,
cookingTime,
sourceUrl,
imageUrl,
isPublished,
price,
tags,
};
const result = await APIRequest(
`http://x.x.x.x:1234/api/courses/${id}`,
"PUT",
JSON.stringify(item)
);
if (result.status !== 200) {
return setAlert(
result.status +
" " +
result.statusText +
" - Please check TextInput fields"
);
}
}
const handleIngredientsChange = (event, index) => {
const shallowCopy = [...ingredients];
shallowCopy[index] = {
...shallowCopy[index],
[event.target.id]: event,
};
setingredients(shallowCopy);
};
return (
<View style={{ minWidth: 70 }}>
<View style={(Styles.bigBar, { textAlign: "center" })}>
<Text> Update recipe</Text>
</View>
<View
style={{
backgroundColor: "#f1f1f1",
padding: 3,
borderRadius: 1,
marginBottom: 7,
}}
>
<View style={{}}>
{alert !== "" && (
<View style={{ display: "flex", justifyContent: "center" }}>
<Alert alert={alert}></Alert>
</View>
)}
<View>
<Text>Name of the course</Text>
<TextInput
style={Styles.formControl}
value={dishName}
onChangeText={e => setdishName(e)}
/>
</View>
<View>
<Text>Category</Text>
<TextInput
style={Styles.formControl}
value={category}
onChangeText={e => setcategory(e)}
/>
</View>
<View>
<Text>Author</Text>
<TextInput
onChangeText={e => setauthor(e)}
style={Styles.formControl}
value={author}
/>
</View>
<View style={{ marginBottom: 2 }}>
<Text>Ingredients</Text>
</View>
{ingredients.map(({ quantity, unit, description }, index) => {
return (
<View style="ing" key={"key" + index}>
<Text>Quantity </Text>
<TextInput
key={"quantity" + index}
style={
(Styles.formControl,
{
marginLeft: 1,
marginRight: 1,
width: 5,
})
}
onChangeText={event => handleIngredientsChange(event, index)}
value={quantity}
/>
<View style={{ marginRight: 2 }}> Unit </View>
<View>
<TextInput
key={"unit" + index}
style={(Styles.formControl, { width: 5 })}
onChangeText={event =>
handleIngredientsChange(event, index)
}
value={unit}
/>
</View>
<View style={{ marginLeft: 1 }}>
{" "}
<Text>Description</Text>{" "}
</View>
<TextInput
key={"description" + index}
style={(Styles.formControl, { marginLeft: 1 })}
onChangeText={event => handleIngredientsChange(event, index)}
value={description}
/>
</View>
);
})}
<View>
<Text>Cooking time</Text>
<TextInput
onChangeText={e => setcookingTime(e)}
style={Styles.formControl}
value={cookingTime}
/>
</View>
<View>
<Text>Source url</Text>
<TextInput
onChangeText={e => setsourceUrl(e)}
style={Styles.formControl}
value={sourceUrl}
/>
</View>
<View>
<Text>Image url</Text>
<TextInput
onChangeText={e => setimageUrl(e)}
style={Styles.formControl}
value={imageUrl}
/>
</View>
<View>
<Text>Publish state</Text>
<TextInput
onChangeText={e => setisPublished(e)}
style={Styles.formControl}
placeholder={"default: true"}
/>
</View>
<View>
<Text>Price</Text>
<TextInput
onChangeText={e => setprice(e)}
style={Styles.formControl}
value={price}
/>
</View>
<View>
<Text>Tags</Text>
<TextInput
onChangeText={e => settags(e)}
style={Styles.formControl}
value={tags}
/>
</View>
<View style={(Styles.prettyB, { textAlign: "center" })}>
<Button onPress={update} title="Submit"></Button>
</View>
</View>
</View>
</View>
);
}
Sample data:
{"isPublished":true,"tags":["pizza","greek"],"_id":"60c9e360b076563888b578a2","dishName":"Greek pizza","category":"pizza","author":"Paolo","ingredients":[{"_id":"60c9e360b076563888b578a3","quantity":0.75,"unit":"cup","description":"warm water"},{"_id":"60c9e360b076563888b578a4","quantity":0.5,"unit":"cup","description":"bread flour"},{"_id":"60c9e360b076563888b578a5","quantity":1.5,"unit":"tea spoon","description":"sea salt"},{"_id":"60c9e360b076563888b578a6","quantity":1,"unit":"cup","description":"baby arugula"},{"_id":"60c9e360b076563888b578a7","quantity":0.67,"unit":"cup","description":"grape tomatoes halved"},{"_id":"60c9e360b076563888b578a8","quantity":1,"unit":"drizzle","description":"olive oil"},{"_id":"60c9e360b076563888b578a9","quantity":1.5,"unit":"tea spoon","description":"dry active yeast"},{"_id":"60c9e360b076563888b578aa","quantity":2,"unit":"tea spoon","description":"sugar"},{"_id":"60c9e360b076563888b578ab","quantity":1,"unit":"lb","description":"hummus any variety"},{"_id":"60c9e360b076563888b578ac","quantity":0.67,"unit":"cup","description":"good pitted greek olives"},{"_id":"60c9e360b076563888b578ad","quantity":0.25,"unit":"cup","description":"crumbled feta cheese"}],"cookingTime":75,"sourceUrl":"https://www.crete.pl/kuchnia-grecka-dania-glowne-wegetarianskie/grecka-pizza-z-feta.html","imageUrl":"https://www.crete.pl/zdjecia/grecka-pizza-z-feta/pizza1.jpg","price":35.99,"date":"2021-06-16T11:41:20.508Z","__v":0}
However it does work fine when I use . It then displays text I created. I do not understand why I cannot put Input tags on this subpage however. Thank you for all help!
You have to use the TextInput , there is no component with the name InputText or Input
https://reactnative.dev/docs/textinput
<Input style={Styles.searchInput} onChangeText={onChange} value={query} />
Should be
<TextInput style={Styles.searchInput} onChangeText={onChange} value={query} />

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)