React Native paper search bar closes keyboard on key press - reactjs

As the title says. Every time I enter a letter into my SearchBar component it closes the keyboard and I'm forced to reopen it by pressing the search bar again. As you can imagine that's quite annoying.
Here's the code for the function component
import React, { useState, useEffect } from "react";
import { View, Text, FlatList } from "react-native";
import { Button, Searchbar } from "react-native-paper";
import { useSelector } from "react-redux";
import {
useFonts,
Poppins_100Thin,
Poppins_100Thin_Italic,
Poppins_200ExtraLight,
Poppins_200ExtraLight_Italic,
Poppins_300Light,
Poppins_300Light_Italic,
Poppins_400Regular,
Poppins_400Regular_Italic,
Poppins_500Medium,
Poppins_500Medium_Italic,
Poppins_600SemiBold,
Poppins_600SemiBold_Italic,
Poppins_700Bold,
Poppins_700Bold_Italic,
Poppins_800ExtraBold,
Poppins_800ExtraBold_Italic,
Poppins_900Black,
Poppins_900Black_Italic,
} from "#expo-google-fonts/poppins";
import Header from "../navigation/Header";
export default function AktSelect({...props}) {
const [data, setData] = useState([]);
const [value, setValue] = useState("");
const [akt, setAkt] = useState([]);
const [ime, setIme] = useState("");
const [opis, setOpis] = useState("");
const [mjesto, setMjesto] = useState("");
const [tip, setTip] = useState("");
const users = useSelector((state) => state.users);
useEffect(() => {
fetch("http://192.168.1.5:8000/fetchActivities", {
method: "POST",
headers: {
Accept: "application/json",
"Content-type": "application/json charset=utf-8",
},
body: JSON.stringify({
team: 'team'
}),
})
.then((res) => res.json())
.then((res) => setAkt(res));
}, []);
fetchData = async () => {
fetch("http://192.168.1.5:8000/fetchActivity", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
data: item_id,
}),
})
.then((res) => res.json())
.then((res) => {
setIme(res[0].title);
setOpis(res[0].description);
setMjesto(res[0].location);
setTip(res[0].activity_type_id);
});
};
searchItems = (text) => {
const newData = akt.filter((item) => {
const itemData = `${item.title.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
setData(newData);
setValue(text);
};
Item = ({ item }) => {
return (
<View>
<Text
style={{
padding: 10,
fontSize: 18,
fontFamily: "Poppins_600SemiBold",
}}
>
{item.title}{" "}
</Text>
<View
style={{
flexDirection: "row",
alignItems: "flex-end",
justifyContent: "flex-end",
}}
>
<Text style={{ padding: 10, fontFamily: "Poppins_400Regular" }}>
{item.start_time}{" "}
</Text>
<Button
mode="outlined"
onPress={() =>
props.navigation.navigate("Izmjena", {
name: item.title,
desc: item.description,
loc: item.location,
type: item.activity_type_id,
item_id: item.id,
})
}
style={{ marginRight: "3%", marginBottom: "1%", color: "#C5272F" }}
color="#C5272F"
>
Dalje
</Button>
</View>
</View>
);
};
renderHeader = () => {
return (
<Searchbar
placeholder="Type here..."
onChangeText={(text) => searchItems(text)}
value={value}
/>
);
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "100%",
backgroundColor: "#CED0CE"
}}
/>
);
};
const { navigation } = props;
return (
<View
style={{
flex: 1,
width: "98%",
alignSelf: "center",
justifyContent: "center",
}}
>
<Header title="Pretraživanje aktivnosti" navigation={navigation} />
<FlatList
data={data}
renderItem={({ item }) => <Item item={item} />}
keyExtractor={(item) => item.id.toString()}
ItemSeparatorComponent={renderSeparator}
ListHeaderComponent={renderHeader}
/>
</View>
);
}
I've converted it from a class component into a function component. Here's the old code
https://hatebin.com/inkmlddkpz

Oddly enough I found an answer not 3 minutes after this post
The issue was I had these 2 functions
renderHeader = () => {
return (
<Searchbar
placeholder="Type here..."
onChangeText={(text) => searchItems(text)}
value={value}
/>
);
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "100%",
backgroundColor: "#CED0CE"
}}
/>
);
};
Which caused a re-render on each keypress of the keyboard (hence the keyboard closing).
The fix was to move them outside the exported function.
function renderSeparator(){
return (
<View
style={{
height: 1,
width: "100%",
backgroundColor: "#CED0CE"
}}
/>
);
}
function renderHeader({value}) {
return (
<Searchbar
placeholder="Type here..."
onChangeText={(text) => searchItems(text)}
value={value}
/>
);
}

Related

two DropDownPicker, one is depnedent on another one

I have two DropDownPicker, one is dependent on another one. If I'm selecting color in one drop down, different colors like yellow, red etc should display on another dropdown but second dropdown is blank and not showing any color. Does any one has any idea how to work with two dependent dropedownpicker.
PLease help me out here. Here is the snack link
https://snack.expo.dev/r-s8KHKyO
Below is my code:
const CarList = () => {
const [data, setData] = useState([]);
const [query, setQuery] = useState('');
const [fullData, setFullData] = useState([]);
const [selected, setSelected] = useState("");
const [open, setOpen] = useState(false);
const [childOpen, setChildOpen] = useState(false);
const [filterOption, setfilteroption] = useState([
{label: 'Model', value: 'model'},
{label: 'Year', value: 'year'},
{label: 'Color', value: 'color'},
]);
const [value, setValue] = useState(null);
const [childItem, setChilditem] = useState(null);
const [childValue, setChildValue] = useState([]);
useEffect(() => {
setIsLoading(true);
fetch(`https://myfakeapi.com/api/cars/?seed=1&page=1&results=20`)
.then(response => response.json())
.then(response => {
setData(response.cars);
setFullData(response.cars);
setIsLoading(false);
})
}, []);
const changeSelectOptionHandler = (item) => {
const colorData = [...new Set(data.map((val) => val.car_color))];
var color = colorData.map(function (val, index) {
return {
id: index,
value: val
}
})
setSelected(item.label);
if (selected === "Color") {
console.log("hi", item.value)
setChildValue(color)
//console is showing it undefined (console.log(setChildValue(color))
}
};
function renderHeader() {
return (
<View>
<TextInput
//some code
/>
<View>
<DropDownPicker onSelectItem={changeSelectOptionHandler}
open={open}
value={value}
items={filterOption}
setOpen={setOpen}
setValue={setValue}
setItems={setfilteroption}
dropDownDirection="TOP"
key={filterOption}
/>
</View>
<View>
<DropDownPicker
open={childOpen}
items={childValue}
value = {childItem}
setValue = {setChilditem}
setOpen={setChildOpen}
setItems={setChildValue.value}
max={10}
dropDownDirection="TOP"
testID="picker-testid"
key={setChildValue.id}
/>
</View>
</View>
);
}
return (
<View style={styles.container}>
//FlatList
</View>
);
}
There are many issues that were causing the second dropdown to not be filled with filtered data.
I refactored the code as below :
import React, { useEffect, useState } from "react";
import {
View,
Text,
Button,
TextInput,
FlatList,
ActivityIndicator,
StyleSheet,
Image,
} from "react-native";
import filter from "lodash.filter";
import DropDownPicker from "react-native-dropdown-picker";
const CarList = () => {
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState([]);
const [error, setError] = useState(null);
const [query, setQuery] = useState("");
const [fullData, setFullData] = useState([]);
const [selected, setSelected] = useState("");
const [open, setOpen] = useState(false);
const [childOpen, setChildOpen] = useState(false);
const [filterOption, setfilteroption] = useState([
{ label: "Model", value: "model" },
{ label: "Year", value: "year" },
{ label: "Color", value: "color" },
]);
const [value, setValue] = useState(null);
const [childItem, setChilditem] = useState(null);
const [childValue, setChildValue] = useState([]);
useEffect(() => {
setIsLoading(true);
fetch(`https://myfakeapi.com/api/cars/?seed=1&page=1&results=20`)
.then((response) => response.json())
.then((response) => {
setData(response.cars);
setFullData(response.cars);
setIsLoading(false);
})
.catch((err) => {
setIsLoading(false);
setError(err);
});
}, []);
if (isLoading) {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<ActivityIndicator size="large" color="#5500dc" />
</View>
);
}
if (error) {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text style={{ fontSize: 18 }}>
Error fetching data... Check your network connection!
</Text>
</View>
);
}
const handleSearch = (text) => {
const formattedQuery = text.toLowerCase();
const filteredData = filter(fullData, (user) => {
return contains(user, formattedQuery);
});
setData(filteredData);
setQuery(text);
};
const contains = ({ car, car_model, car_color }, query) => {
if (
car.toLowerCase().includes(query) ||
car_model.toLowerCase().includes(query) ||
car_color.toLowerCase().includes(query)
) {
return true;
}
return false;
};
const changeSelectOptionHandler = (item) => {
const colorData = [...new Set(data.map((val) => val.car_color))];
const modelData = [...new Set(data.map((val) => val.car_model))];
const yearData = [...new Set(data.map((val) => val.car_model_year))];
var color = colorData.map(function (val, index) {
return {
id: index,
value: val,
label: val,
};
});
var model = modelData.map(function (val, index) {
return {
id: index,
value: val,
label: val,
};
});
var year = yearData.map(function (val, index) {
return {
id: index,
value: val,
label: val,
};
});
setSelected(item.label);
if (selected === "Color") {
setChildValue(color);
} else if (selected === "Model") {
setChildValue(model);
} else if (selected === "Year") {
setChildValue(year);
}
};
function renderHeader() {
return (
<View
style={{
backgroundColor: "#fff",
padding: 10,
marginVertical: 10,
borderRadius: 20,
}}
>
<TextInput
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="always"
value={query}
onChangeText={(queryText) => handleSearch(queryText)}
placeholder="Search"
style={{ backgroundColor: "#fff", paddingHorizontal: 20 }}
/>
<View>
<DropDownPicker
onSelectItem={changeSelectOptionHandler}
open={open}
value={value}
items={filterOption}
setOpen={setOpen}
setValue={setValue}
setItems={setfilteroption}
dropDownDirection="TOP"
key={filterOption}
zIndex={1000}
zIndexInverse={3000}
testID="picker-testid"
style={{
padding: 5,
margin: 5,
width: 200,
flexDirection: "row",
// borderRadius: 20
}}
/>
</View>
<View>
<DropDownPicker
open={childOpen}
items={childValue}
value={childItem}
setValue={setChilditem}
setOpen={setChildOpen}
setItems={childValue}
max={10}
dropDownDirection="TOP"
testID="picker-testid"
style={{
padding: 10,
margin: 10,
width: 200,
flexDirection: "row",
// borderRadius: 20
}}
/>
</View>
</View>
);
}
return (
<View style={styles.container}>
<Text style={styles.text}>Favorite Contacts</Text>
<FlatList
keyboardShouldPersistTaps="always"
ListHeaderComponent={renderHeader}
data={data}
keyExtractor={({ id }) => id}
renderItem={({ item }) => (
<View style={styles.listItem}>
<Image
source={{
uri: "https://picsum.photos/200",
}}
style={styles.coverImage}
/>
<View style={styles.metaInfo}>
<Text
style={styles.title}
>{`${item.car} ${item.car_model}`}</Text>
<Text>Color: {`${item.car_color}`}</Text>
<Text>Price: {`${item.price}`}</Text>
</View>
</View>
)}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: "#f8f8f8",
alignItems: "center",
},
text: {
fontSize: 20,
color: "#101010",
marginTop: 60,
fontWeight: "700",
},
listItem: {
marginTop: 10,
paddingVertical: 20,
paddingHorizontal: 20,
backgroundColor: "#fff",
flexDirection: "row",
},
coverImage: {
width: 100,
height: 100,
borderRadius: 8,
},
metaInfo: {
marginLeft: 10,
},
title: {
fontSize: 18,
width: 200,
padding: 10,
},
});
export default CarList;
You can also check the working demo here - https://snack.expo.dev/#emmbyiringiro/4a3bc4

How to display a value back in parent component in react native?

I have a page which shows list of coins.
From there I navigated to another component and passing the coin name as param.
From the second page, I have filtered all the orders based on the coin name received in params.Here, I calculated the average value. How to pass this back to parent page so that I can see the average value of orders beside each coin?
//screen1 code:
import React, { useEffect, useState } from "react";
import {
FlatList,
Keyboard,
Text,
TextInput,
TouchableOpacity,
View,
StyleSheet,
TouchableWithoutFeedback,
} from "react-native";
import { SwipeListView } from "react-native-swipe-list-view";
import styles from "./styles";
import { firebase } from "../../firebase/config";
export default function CoinCreate(props) {
const [coinText, setCoinText] = useState("");
const [coins, setCoins] = useState([]);
const coinRef = firebase.firestore().collection("coins");
const userID = props.extraData.id;
useEffect(() => {
coinRef
.where("authorID", "==", userID)
.orderBy("createdAt", "desc")
.onSnapshot(
(querySnapshot) => {
const newCoins = [];
querySnapshot.forEach((doc) => {
const coin = doc.data();
coin.id = doc.id;
newCoins.push(coin);
});
setCoins(newCoins);
},
(error) => {
console.log(error);
}
);
}, []);
const onAddButtonPress = () => {
if (coinText && coinText.length > 0) {
const timestamp = firebase.firestore.FieldValue.serverTimestamp();
const data = {
text: coinText,
authorID: userID,
createdAt: timestamp,
};
coinRef
.add(data)
.then((_doc) => {
setCoinText("");
Keyboard.dismiss();
})
.catch((error) => {
alert(error);
});
}
};
const renderCoin = ({ item, index }) => {
return (
<View style={styles1.rowFront}>
<TouchableWithoutFeedback
onPress={() =>
props.navigation.navigate("Orders", {
coin: item.text,
userID: userID,
})
}
>
<Text>
{index}. {item.text}
</Text>
</TouchableWithoutFeedback>
</View>
);
};
return (
<View style={styles.container}>
<View style={styles.formContainer}>
<TextInput
style={styles.input}
placeholder="Add new coin"
placeholderTextColor="#aaaaaa"
onChangeText={(text) => setCoinText(text)}
value={coinText}
underlineColorAndroid="transparent"
autoCapitalize="none"
/>
<TouchableOpacity style={styles.button} onPress={onAddButtonPress}>
<Text style={styles.buttonText}>Add</Text>
</TouchableOpacity>
</View>
{coins && (
<SwipeListView
data={coins}
keyExtractor={(item) => item.id}
renderItem={renderCoin}
removeClippedSubviews={true}
/>
)}
</View>
);
}
const styles1 = StyleSheet.create({
rowFront: {
alignItems: "center",
backgroundColor: "#FFF",
borderBottomWidth: 0.25,
justifyContent: "center",
height: 50,
},
rowBack: {
alignItems: "center",
backgroundColor: "#DDD",
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
paddingLeft: 15,
},
backRightBtn: {
alignItems: "center",
bottom: 0,
justifyContent: "center",
position: "absolute",
top: 0,
width: 75,
backgroundColor: "red",
right: 0,
},
});
//screen2 code:
import React, { useEffect, useState } from "react";
import {
FlatList,
Keyboard,
Text,
TextInput,
TouchableOpacity,
View,
StyleSheet,
} from "react-native";
import { Avatar, Button, Card, Title, Paragraph } from "react-native-paper";
import { SwipeListView } from "react-native-swipe-list-view";
import styles from "./styles";
import { firebase } from "../../firebase/config";
import { Icon } from "react-native-elements";
import { createIconSetFromFontello } from "#expo/vector-icons";
export default function OrderList(props) {
const LeftContent = (props) => <Avatar.Icon {...props} icon="folder" />;
const [orderText, setOrderText] = useState("");
const [orders, setOrders] = useState([]);
const orderRef = firebase.firestore().collection("orders");
const userID = props.route.params.userID;
const coin = props.route.params.coin;
//averageValue = (totalCost / totalCount).toString();
const [averageValue, setAverageValue] = useState("");
useEffect(() => {
orderRef
.where("authorID", "==", userID)
.where("name", "==", coin)
.orderBy("createdAt")
.onSnapshot(
(querySnapshot) => {
const newOrders = [];
querySnapshot.forEach((doc) => {
const order = doc.data();
order.id = doc.id;
newOrders.push(order);
});
setOrders(newOrders);
},
(error) => {
console.log(error);
}
);
}, []);
useEffect(() => {
//calculate and set anything like totalCost, averageValue, etc here
console.log("---came to orders effect---");
//console.log(orders);
let totalCost = 0;
let totalCount = 0;
orders.forEach((item, index) => {
console.log(item);
console.log(index);
totalCost += parseFloat(item.amount);
totalCount += parseFloat(item.count);
});
setAverageValue((totalCost / totalCount).toString());
}, [orders]);
/*
useEffect(() => {
let avg = (parseFloat(totalCost) / parseFloat(totalCount)).toString();
console.log("Avg:" + avg);
setAverageValue(avg);
}, [totalCount, totalCost]);
*/
const onAddButtonPress = () => {
props.navigation.navigate("CreateOrder", {
coin: coin,
userID: userID,
orderRef,
});
};
const renderOrder = ({ item, index }) => {
//console.log("----------------------");
//console.log(item.createdAt.toDate().toString());
//console.log("----------------------");
//setTotalCost(parseFloat(totalCost) + parseFloat(item.price));
//setTotalCount(parseFloat(totalCount) + parseFloat(item.count));
//totalCost = parseFloat(totalCost) + parseFloat(item.price);
//totalCount = parseFloat(totalCount) + parseFloat(item.count);
//console.log(totalCost);
//console.log(totalCount);
return (
<View style={styles1.rowFront}>
<Text>
{index}. {item.price} {item.amount} {item.count}
{"\n" + item.createdAt.toDate().toString()}
</Text>
<Icon name={"flight-takeoff"} />
</View>
);
};
return (
<View style={styles.container}>
<TouchableOpacity style={styles.button} onPress={onAddButtonPress}>
<Text style={styles.buttonText}>Click here to create new order..</Text>
</TouchableOpacity>
{orders.length === 0 && (
<Text>Please order some coins in currency: {coin}</Text>
)}
{orders && orders.length > 0 && (
<>
<Text>Average Value: {averageValue}</Text>
<SwipeListView
data={orders}
keyExtractor={(item) => item.id}
renderItem={renderOrder}
removeClippedSubviews={true}
/>
</>
)}
</View>
);
}
const styles1 = StyleSheet.create({
rowFront: {
alignItems: "center",
backgroundColor: "#FFF",
borderBottomWidth: 0.25,
justifyContent: "center",
height: 50,
},
rowBack: {
alignItems: "center",
backgroundColor: "#DDD",
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
paddingLeft: 15,
},
backRightBtn: {
alignItems: "center",
bottom: 0,
justifyContent: "center",
position: "absolute",
top: 0,
width: 75,
backgroundColor: "red",
right: 0,
},
});
To simplify the process, I created a normal function(not react component) with orderListData(coin,userID) in a separate file and imported that and calling that function from the screen 1, but getting error as
TypeError: (0, _OrderListData.orderListData) is not a function. (In '(0, _OrderListData.orderListData)("ada", "nMOpBupcptZF8YlxiJYFX7vPMtC2")', '(0, _OrderListData.orderListData)' is undefined)
My code:
OrderListData.js
import { firebase } from "../../firebase/config";
export default function orderListData(coin, userID) {
let orders = [];
const orderRef = firebase.firestore().collection("orders");
//const userID = props.route.params.userID;
//const coin = props.route.params.coin;
let totalCost = 0;
let totalCount = 0;
let averagePrice = 0;
orderRef
.where("authorID", "==", userID)
.where("name", "==", coin)
.orderBy("createdAt")
.onSnapshot(
(querySnapshot) => {
querySnapshot.forEach((doc) => {
const order = doc.data();
order.id = doc.id;
orders.push(order);
});
},
(error) => {
console.log(error);
}
);
orders.forEach((item, index) => {
totalCost += parseFloat(item.amount);
totalCount += parseFloat(item.count);
});
averagePrice = totalCost / totalCount;
return {
averagePrice,
totalCost,
totalCount,
};
}
And the modified screen 1:
import React, { useEffect, useState } from "react";
import {
FlatList,
Keyboard,
Text,
TextInput,
TouchableOpacity,
View,
StyleSheet,
TouchableWithoutFeedback,
} from "react-native";
import { SwipeListView } from "react-native-swipe-list-view";
import styles from "./styles";
import { firebase } from "../../firebase/config";
import { orderListData } from "./OrderListData";
export default function CoinCreate(props) {
console.log("testing");
console.log(orderListData("ada", "nMOpBupcptZF8YlxiJYFX7vPMtC2"));
const [coinText, setCoinText] = useState("");
const [coins, setCoins] = useState([]);
const coinRef = firebase.firestore().collection("coins");
const userID = props.extraData.id;
useEffect(() => {
coinRef
.where("authorID", "==", userID)
.orderBy("createdAt", "desc")
.onSnapshot(
(querySnapshot) => {
const newCoins = [];
querySnapshot.forEach((doc) => {
const coin = doc.data();
coin.id = doc.id;
newCoins.push(coin);
});
setCoins(newCoins);
},
(error) => {
console.log(error);
}
);
}, []);
const onAddButtonPress = () => {
if (coinText && coinText.length > 0) {
const timestamp = firebase.firestore.FieldValue.serverTimestamp();
const data = {
text: coinText,
authorID: userID,
createdAt: timestamp,
};
coinRef
.add(data)
.then((_doc) => {
setCoinText("");
Keyboard.dismiss();
})
.catch((error) => {
alert(error);
});
}
};
const renderCoin = ({ item, index }) => {
let { averagePrice, totalCost, totalCount } = orderListData(
item.text,
userID
);
return (
<View style={styles1.rowFront}>
<TouchableWithoutFeedback
onPress={() =>
props.navigation.navigate("Orders", {
coin: item.text,
userID: userID,
})
}
>
<Text>
{index}. {item.text} {averagePrice}
</Text>
</TouchableWithoutFeedback>
</View>
);
};
return (
<View style={styles.container}>
<View style={styles.formContainer}>
<TextInput
style={styles.input}
placeholder="Add new coin"
placeholderTextColor="#aaaaaa"
onChangeText={(text) => setCoinText(text)}
value={coinText}
underlineColorAndroid="transparent"
autoCapitalize="none"
/>
<TouchableOpacity style={styles.button} onPress={onAddButtonPress}>
<Text style={styles.buttonText}>Add</Text>
</TouchableOpacity>
</View>
{coins && (
<SwipeListView
data={coins}
keyExtractor={(item) => item.id}
renderItem={renderCoin}
removeClippedSubviews={true}
/>
)}
</View>
);
}
const styles1 = StyleSheet.create({
rowFront: {
alignItems: "center",
backgroundColor: "#FFF",
borderBottomWidth: 0.25,
justifyContent: "center",
height: 50,
},
rowBack: {
alignItems: "center",
backgroundColor: "#DDD",
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
paddingLeft: 15,
},
backRightBtn: {
alignItems: "center",
bottom: 0,
justifyContent: "center",
position: "absolute",
top: 0,
width: 75,
backgroundColor: "red",
right: 0,
},
});
Any suggestion on this please.
There are different ways to solve this problem,
Your requirement is to update one screen based on another screen, so you can think of using something state management for this but in your case you have the trigger of going back so use the functions of the navigation library to do that.
So I'll put a working example here using a simple scenario of sending an array
Let say that you have a home screen, from here we go to a CreateOrder screen and it will send data do this screen via navigation.navigate (navigate function will navigate to an existing screen or push a new screen so here you will be sent back).
function HomeScreen({ navigation, route }) {
React.useEffect(() => {
if (route.params?.orders) {
}
}, [route.params?.orders]);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="Create post"
onPress={() => navigation.navigate('CreateOrder')}
/>
<Text style={{ margin: 10 }}>Post: {route.params?.orders?.length}</Text>
</View>
);
}
You can use the useEffect to update if the param is updated and use the route prop.
The second screen would override the back button and also have a button to navigate back. Here we send the orders array via navigation.navigate
function CreateOrdersScreen({ navigation, route }) {
const [orders, setOrders] = React.useState('');
React.useLayoutEffect(() => {
navigation.setOptions({
headerLeft: (props) => (
<HeaderBackButton
{...props}
onPress={() => {
navigation.navigate('Home', { orders });
}}
/>
),
});
}, [navigation, orders]);
return (
<>
<Text>{orders.length}</Text>
<Button
title="Add"
onPress={() => {
setOrders([...orders, 1]);
}}
/>
<Button
title="Done"
onPress={() => {
// Pass and merge params back to home screen
navigation.navigate({
name: 'Home',
params: { orders },
merge: true,
});
}}
/>
</>
);
}
You can see a running example here
https://snack.expo.io/#guruparan/5b84d0
References
https://reactnavigation.org/docs/params/#passing-params-to-a-previous-screen
https://reactnavigation.org/docs/header-buttons/#overriding-the-back-button

React Native: How to show the result of the newly added data immediately without reloading the page

In React Native, React Native: How to show the result of the newly added data immediately without reloading the page? in my current status, I can add data successfully but I have to refresh the page to reflect in my browser the newest or updated data in my database. this is my code
import Profile from "../../components/profile";
import AddProfile from "../../components/AddProfile";
const [profilemanager, setProfileManager] = useState([]);
const { data, refetch, isLoading, isError, error } = useQuery('userData', async()=> {
const _token = await AsyncStorage.getItem('#token');
setToken(_token);
await getProfileList(_token);
});
const getProfileList= (_token) =>{
fetch(config.API_URL + '/profileManager/500/1', {
method: 'GET',
//body: JSON.stringify(dataToSend),
headers: {
//Header Defination
'Authorization': 'Bearer ' + _token,
'Content-Type': 'application/json',
},
})
.then((response) => response.json())
.then((responseJson) => {
setProfileManager(responseJson);
});
}
const addUserdataHandler = (selectedUserId) => {
fetch(config.API_URL + '/profileManager/add', {
method: 'POST',
body: JSON.stringify({intuserid: selectedUserId}),
headers: {
//Header Defination
'Authorization': 'Bearer '+ token,
'Content-Type': 'application/json',
},
})
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
if(responseJson.hasOwnProperty("errors")){
console.log("errors",responseJson);
setSavingError(JSON.stringify(responseJson.errors));
}else{
setProfileManager(prevDMSMANAGER => [
...prevPROFILEMANAGER,
{ usersid:selectedUserId}
]);
}
}).catch(function(error) {
console.log(error);
});
};
<AddProfile
onAddProfile={addUserdataHandler}
/>
<Text>{savingError}</Text>
<View style={{zIndex:-1}}>
<FlatList
style={{minHeight:200,maxHeight:300, width:550}}
keyExtractor={(item, index) =>index.toString()}
data={profilemanager}
renderItem={({ item }) => (
<Profile
key={item.usersid}
id={item.usersid}
Lastname={item.lastname}
Firstname={item.firstname}
Middlename={item.middlename}
pressHandler={confirmDelete}
/>
)}
/>
</View>
this is from my Profile.js
const Profile = props => {
const [text, setText] = useState(props.title);
const [isEditing, setEdit] = useState(false);
const handleEdit = (dataToSend) => {
props.pressHandler(dataToSend, props.id);
console.log("props.id: ", props.id)
setEdit(false);
};
return (
<View style={styles.items}>
<View style={styles.itemContainer}>
<Formik
initialValues={{
id:props.id,
Firstname:props.Firstname,
Lastname:props.Lastname,
Middlename:props.Middlename
}}
onSubmit={handleEdit}
style={{flexWrap:'wrap'}}
>
{({ handleChange, handleBlur, handleSubmit,isSubmitting, values, errors, isValid,touched,setFieldValue }) => (
<>
<Text style={styles.itemText}>{props.Lastname} {props.Firstname}, {props.Middlename}</Text>
<View style={styles.btnContainer}>
<Button title="Remove" onPress={handleSubmit} style={styles.editBtn} />
</View>
{(errors.UsersId && touched.UsersId) && <View style={{flex:1,flexDirection:'column', flexBasis:'100%'}}></View>}
</>
)}
</Formik>
</View>
</View>
);
};
This is how I handled the add profile
const addTodoHandler =async (dataToSend, { resetForm}) => {
console.log("dataToSend: ", dataToSend.selectedUserId)
await props.onAddProfile(dataToSend.selectedUserId);
resetForm({})
};
return (
<View >
<Formik
enableReinitialize
validationSchema={ValidationSchema}
initialValues={{
selectedUserId:0
}}
onSubmit={addTodoHandler}
>
{({ handleChange, handleBlur,handleReset,resetForm, handleSubmit,isSubmitting, values, errors, isValid,touched,setFieldValue }) => (
<>
<View style={forms.fieldRow}>
{/* <TouchableWithoutFeedback onPress={() => dropDownRef.current.close()}> */}
<View style={{padding:0, width:250,flexDirection:'row',flexGrow:0}}>
<DropDownPicker
name="selectedUserId"
value={selectedUserId}
defaultValue={selectedUserId}
zIndex={9000}
controller={instance => dropDownRef.current = instance}
searchable={true}
placeholder="Select a user"
searchablePlaceholder="Search a user.."
searchablePlaceholderTextColor="gray"
seachableStyle={{maxHeight:500, height:500}}
searchableError={() => <Text>Not Found</Text>}
onSearch={text => { searchUser(text) }}
items={users}
//defaultValue={country}
containerStyle={forms.dropdownStyle}
style={{
backgroundColor: '#fff',
width:230,
paddingTop:10,
borderColor:'#dadae8',
overflow:'hidden'}}
itemStyle={{
justifyContent: 'flex-start',
}}
dropDownStyle={{backgroundColor: '#fff', padding:10, width:200, height:400, maxHeight:500,borderStyle:'solid', zIndex:999, borderColor:'#444', shadowColor: '#000',
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.22,
shadowRadius: 2.22,
elevation: 3,}}
onChangeItem={item => {
setFieldValue("selectedUserId",item.value)
setFieldValue("selectedUserName",item.label)
setSelectedUserId(item.value);
}}
/>
</View>
<View style={{padding:0,zIndex:-1,flexDirection:'row',flexGrow: 1}}>
<Button title="Add" onPress={handleSubmit} disabled={!isValid}/>
</View>
</View>
<View style={{zIndex:-1}}>
{(errors.selectedUserId && touched.selectedUserId) && <Text style={formStyles.errorText}>{errors.selectedUserId}
</Text> }
</View>
</>
)}
</Formik>
</View>
);
};

React Native Hooks Search Filter FlatList

I am trying to make a Covid19 React Native Expo app. It contains a search filter from which user will select a country then the selected country results will be shown to the user. I keep getting this error on my Android device "Unexpected Identifier You" while on web pack the countries load but they don't filter correctly.
Working Snack Link: https://snack.expo.io/#moeez71/ac5758
Here is my code:
import React, { useState, useEffect } from "react";
import {
ActivityIndicator,
Alert,
FlatList,
Text,
StyleSheet,
View,
TextInput,
} from "react-native";
export default function ABCDEE() {
const [arrayholder, setArrayholder] = useState([]);
const [text, setText] = useState("");
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const fetchAPI = () => {
return fetch("https://api.covid19api.com/countries")
.then((response) => response.json())
.then((responseJson) => {
setData(responseJson);
setLoading(false);
setArrayholder(responseJson);
})
.catch((error) => {
console.error(error);
});
};
useEffect(() => {
fetchAPI();
});
const searchData = (text) => {
const newData = arrayholder.filter((item) => {
const itemData = item.Country.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
setData(newData);
setText(text);
};
const itemSeparator = () => {
return (
<View
style={{
height: 0.5,
width: "100%",
backgroundColor: "#000",
}}
/>
);
};
return (
<View>
{loading === false ? (
<View style={styles.MainContainer}>
<TextInput
style={styles.textInput}
onChangeText={(text) => searchData(text)}
value={text}
underlineColorAndroid="transparent"
placeholder="Search Here"
/>
<FlatList
data={data}
keyExtractor={(item, index) => index.toString()}
ItemSeparatorComponent={itemSeparator}
renderItem={({ item }) => (
<Text style={styles.row}>{item.Country}</Text>
)}
style={{ marginTop: 10 }}
/>
</View>
) : (
<Text>loading</Text>
)}
</View>
);
}
const styles = StyleSheet.create({
MainContainer: {
paddingTop: 50,
justifyContent: "center",
flex: 1,
margin: 5,
},
row: {
fontSize: 18,
padding: 12,
},
textInput: {
textAlign: "center",
height: 42,
borderWidth: 1,
borderColor: "#009688",
borderRadius: 8,
backgroundColor: "#FFFF",
},
});
You had made two mistakes in the above code
useEffect second parameter should be a empty array for it act as componentDidMount()
useEffect(() => {
fetchAPI();
},[])
in FlatList renderItem need to destructure the item.
renderItem={( {item} ) => <Text style={styles.row}
>{item.Country}</Text>}
Working code
import React, {useState, useEffect} from "react"
import { ActivityIndicator, Alert, FlatList, Text, StyleSheet, View, TextInput } from 'react-native';
export default function ABCDEE(){
const [arrayholder,setArrayholder] =useState([])
const[text, setText] = useState('')
const[data, setData] = useState([])
const [loading , setLoading] = useState(true)
const fetchAPI = ()=> {
return fetch('https://api.covid19api.com/countries')
.then((response) => response.json())
.then((responseJson) => {
setData(responseJson)
setLoading(false)
setArrayholder(responseJson)
}
)
.catch((error) => {
console.error(error);
});
}
useEffect(() => {
fetchAPI();
},[])
const searchData= (text)=> {
const newData = arrayholder.filter(item => {
const itemData = item.Country.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1
});
setData(newData)
setText(text)
}
const itemSeparator = () => {
return (
<View
style={{
height: .5,
width: "100%",
backgroundColor: "#000",
}}
/>
);
}
return (
<View style={{flex:1}} >
{loading === false ?
<View style={styles.MainContainer}>
<TextInput
style={styles.textInput}
onChangeText={(text) => searchData(text)}
value={text}
underlineColorAndroid='transparent'
placeholder="Search Here" />
<FlatList
data={data}
keyExtractor={ (item, index) => index.toString() }
ItemSeparatorComponent={itemSeparator}
renderItem={( {item} ) => <Text style={styles.row}
>{item.Country}</Text>}
style={{ marginTop: 10 }} />
</View>
: <Text>loading</Text>}
</View>
);
}
const styles = StyleSheet.create({
MainContainer: {
paddingTop: 50,
justifyContent: 'center',
flex: 1,
margin: 5,
},
row: {
fontSize: 18,
padding: 12
},
textInput: {
textAlign: 'center',
height: 42,
borderWidth: 1,
borderColor: '#009688',
borderRadius: 8,
backgroundColor: "#FFFF"
}
});
There are multiple issues with your implementation. I will point out some mistake/ignorance. You can clean up you code accordingly.
Do not create 2 state to keep same data. ie. arrayholder and data.
Change text value on search, don't the data. based on that text filter
Hooks always define some variable to be watched.
Update: Seems there is an issue with flex in android view, i use fixed height it is visible.
Just a hack for android issue. minHeight
MainContainer: {
paddingTop: 50,
justifyContent: 'center',
flex: 1,
margin: 5,
minHeight: 800,
},
Working link: https://snack.expo.io/kTuT3uql_
Updated code:
import React, { useState, useEffect } from 'react';
import {
ActivityIndicator,
Alert,
FlatList,
Text,
StyleSheet,
View,
TextInput,
} from 'react-native';
export default function ABCDEE() {
const [text, setText] = useState('');
const [state, setState] = useState({ data: [], loading: false }); // only one data source
const { data, loading } = state;
const fetchAPI = () => {
//setState({data:[], loading: true});
return fetch('https://api.covid19api.com/countries')
.then(response => response.json())
.then(data => {
console.log(data);
setState({ data, loading: false }); // set only data
})
.catch(error => {
console.error(error);
});
};
useEffect(() => {
fetchAPI();
}, []); // use `[]` to avoid multiple side effect
const filterdData = text // based on text, filter data and use filtered data
? data.filter(item => {
const itemData = item.Country.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
})
: data; // on on text, u can return all data
console.log(data);
const itemSeparator = () => {
return (
<View
style={{
height: 0.5,
width: '100%',
backgroundColor: '#000',
}}
/>
);
};
return (
<View>
{loading === false ? (
<View style={styles.MainContainer}>
<TextInput
style={styles.textInput}
onChangeText={text => setText(text)}
value={text}
underlineColorAndroid="transparent"
placeholder="Search Here"
/>
<FlatList
data={filterdData}
keyExtractor={(item, index) => index.toString()}
ItemSeparatorComponent={itemSeparator}
renderItem={({ item }) => (
<Text style={styles.row}>{item.Country}</Text>
)}
style={{ marginTop: 10 }}
/>
</View>
) : (
<Text>loading</Text>
)}
</View>
);
}
const styles = StyleSheet.create({
MainContainer: {
paddingTop: 50,
justifyContent: 'center',
//flex: 1,
margin: 5,
height: 800,
},
row: {
fontSize: 18,
padding: 12,
},
textInput: {
textAlign: 'center',
height: 42,
borderWidth: 1,
borderColor: '#009688',
borderRadius: 8,
backgroundColor: '#333',
},
});
It seems like the error comes from the catch block when you fetch your data. The response comes with a 200 status, so it's not an issue with the endpoint itself. I console.logged the response and it seems fine. The problem is when you parse the response and try to use it in the second then() block, so the catch block fires up. I could not debug it right in the editor you use, but I would check what type of object I'm receiving from the API call.
This is not a direct answer to your question but I didn't want this to get lost in the comments as it is directly related with your efforts on this app.
App Store and Google Play Store no longer accept apps that have references to Covid-19 https://developer.apple.com/news/?id=03142020a
You can only publish apps about Covid-19 if you are reputable source like a government's health department.
Therefore, I urge you to reevaluate your efforts on this app.

I keep getting this error. TypeError: addItems is not a function. (In 'addItems(text)', 'addItems' is an instance of Object)

This is the error I get every time I try and add something to my list.
TypeError: addItems is not a function. (In 'addItems(text)', 'addItems' is an instance of Object)
I cannot figure out what I am doing wrong. Im new to react-native so any help would be greatly appreciated.
Here is my App.js
import React, { useState } from 'react';
import { View, Text, StyleSheet, ImageBackground, FlatList } from 'react-native';
import 'react-native-get-random-values';
import { v4 as uuidv4 } from 'uuid';
import { uuid } from 'uuidv4';
import Header from './Componets/Header';
import AddItem from './Componets/AddItem';
import background from './Images/sunrise-in-the-smoky-mountains.jpg';
import ListItem from './Componets/ListItem';
const App = () => {
const [item, setItems] = useState([
// {
// id: uuidv4(),
// name: ''
// },
{
id: uuidv4(),
name: "gym"
},
{
id: uuidv4(),
name: "study"
}
]);
const addItems = (text) => {
setItems(prevItems => {
return [{ id: uuidv4(), text }, ...prevItems]
})
}
const deleteItem = (id) => {
setItems(prevVal => {
return prevVal.filter(item => item.id != id)
})
}
return (
<View style={styles.container}>
<ImageBackground source={background} style={styles.image}>
<Header
title="Gotta get this done!" />
<AddItem
addItem={addItems}
/>
<FlatList
data={item}
renderItem={({ item }) => (
<ListItem item={item.name} deleteItem={deleteItem} />
)}
/>
</ImageBackground>
</View>
)
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
image: {
flex: 1,
resizeMode: "cover",
justifyContent: "center"
}
})
export default App;
Here is where I call the function and get the error.
import React, { useState } from 'react';
import { View, Text, StyleSheet, TextInput, TouchableOpacity, Button } from 'react-native';
const AddItem = ( addItems ) => {
const [text, setText] = useState("");
const onChange = (inputVal) => setText(inputVal);
return (
<View style={styles.addItemView}>
<TextInput
style={styles.inputText}
placeholder="Add item to list..."
onChangeText={onChange}
/>
<TouchableOpacity style={styles.button}>
<Button title="Add Item" onPress={() => addItems(text)} />
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
addItemView: {
flex: 1,
flexDirection: "row",
alignItems: 'center'
},
inputText: {
fontSize: 20,
backgroundColor: "white",
alignItems: 'center',
justifyContent: 'center',
borderWidth: 1,
width: 250,
},
button: {
alignItems: "center",
justifyContent: "center",
height: 40,
backgroundColor: "#bbc7ad",
borderRadius: 10,
borderWidth: 2,
borderColor: "#99a191",
marginLeft: 20,
}
})
export default AddItem;
You are passing addItems function as a prop in
<AddItem
addItem={addItems}
/>
When you pass any function or value to any functional component in React you can access them only via props so to access addItems you need to access this function from the props
You can do that by following ways
Method 1
const AddItem = ( props ) => {
// We are extracting addItem function from props
const {addItem} = props
const [text, setText] = useState("");
const onChange = (inputVal) => setText(inputVal);
return (
<View style={styles.addItemView}>
<TextInput
style={styles.inputText}
placeholder="Add item to list..."
onChangeText={onChange}
/>
<TouchableOpacity style={styles.button}>
<Button title="Add Item" onPress={() => addItem(text)} />
</TouchableOpacity>
</View>
);
};
Method 2 :
const AddItem = ({addItem} ) => {
.....
return (
<View style={styles.addItemView}>
......
<TouchableOpacity style={styles.button}>
<Button title="Add Item" onPress={() => addItem(text)} />
</TouchableOpacity>
</View>
);
};
This seems to work for now. Still unclear as to why using destructuring in my original version didnt work. Please chime in if you can help with that. Thanks!
const AddItem = (props) => {
const [text, setText] = useState("");
const onChange = inputVal => setText(inputVal);
return (
<View style={styles.addItemView}>
<TextInput
style={styles.inputText}
placeholder="Add item to list..."
onChangeText={onChange}
/>
<TouchableOpacity style={styles.button}>
<Button title="Add Item" onPress={() => props.addItem(text)} />
</TouchableOpacity>
</View>
);
};

Resources