Search engine in Expo - react-native

I follow this tutorial to add search to my Expo (React Native) app. After the last step I have this mistake:photo.
What should I do?
This is the part of the program.
This is one of my navigation screens, where I have links for other screens:
function InfoScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Go back" onPress={() => navigation.goBack()} />
<View style={styles.container_new}>
<Text style={styles.text}>Basic FlatList Example</Text>
<FlatList
ListHeaderComponent={renderHeader}
...
</View>
)}
/>
</View>
</View>
);
}
Function renderHeader from the tutorial:
function renderHeader() {
return (
...
);
}
Part from the tutorial (last steps)
const [query, setQuery] = useState('');
const [fullData, setFullData] = useState([]);
useEffect(() => {
setIsLoading(true);
fetch(API_ENDPOINT)
.then(response => response.json())
.then(response => {
setData(response.results);
// ADD THIS
setFullData(response.results);
setIsLoading(false);
})
.catch(err => {
setIsLoading(false);
setError(err);
});
}, []);
const handleSearch = text => {
const formattedQuery = text.toLowerCase();
const filteredData = filter(fullData, user => {
return contains(user, formattedQuery);
});
setData(filteredData);
setQuery(text);
};
const contains = ({ name, email }, query) => {
const { first, last } = name;
if (first.includes(query) || last.includes(query) || email.includes(query)) {
return true;
}
return false;
};

Related

React Native - Render FlatList using API data

I'm trying to get data from an external API and then render it into a flatlist.
I'm very new to React Native so this may be easy to solve.
I'm trying to use the following data: https://www.nationaltrust.org.uk/search/data/all-places
I want to fetch it from the URL, and render the 'title' and 'imageUrl' fields into a flatlist component.
This is what I have so far:
const placesURL = "https://www.nationaltrust.org.uk/search/data/all-places";
const [isLoading, setLoading] = useState(true);
const [places, setPlaces] = useState([]);
useEffect(() => {
fetch(placesURL)
.then((response) => response.json())
.then((json) => setPlaces(json))
.catch((error) => alert(error))
.finally(setLoading(false));
})
And in the flatlist:
export default function App() {
return (
<View style={styles.container}>
<FlatList
data={places}
renderItem={({ item }) => (
<Text>{item.title}</Text>
)}
keyExtractor={(item) => item.id}
/>
<StatusBar style="auto" />
</View>
);
}
If anyone could tell me what to do I would really appreciate it.
try updating your useEffect hook to this
useEffect(() => {
if(places.length === 0 && isLoading){
fetch(placesURL)
.then((response) => response.json())
.then((json) => setPlaces(json))
.catch((error) => alert(error))
.finally(setLoading(false));
}
}, [places, isLoading])
and
export default function App() {
return (
<View style={styles.container}>
{places.length !== 0 &&
<FlatList
data={places}
renderItem={({ item }) => (
<Text>{item.title}</Text>
)}
keyExtractor={(item) => item.id}
/>
}
<StatusBar style="auto" />
</View>
);
}
This URL https://www.nationaltrust.org.uk/search/data/all-places returns a JSON object not an array of objects. It's required to transform an object into an array of objects to be compatible with FlatList.
import React, { useState, useEffect } from "react";
import { Text, View, StyleSheet, FlatList } from "react-native";
const placesURL = "https://www.nationaltrust.org.uk/search/data/all-places";
export default function App() {
const [isLoading, setLoading] = useState(true);
const [places, setPlaces] = useState([]);
const getPlaces = async () => {
try {
const response = await fetch(placesURL);
const result = await response.json();
const newPlaces = Object.values(result);
setPlaces(newPlaces);
setLoading(false);
} catch (error) {
setLoading(false);
console.log(error);
}
};
useEffect(() => {
getPlaces();
}, []);
if (isLoading) {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text> Searching places.... </Text>
</View>
);
}
return (
<View style={styles.container}>
<FlatList
data={places}
renderItem={({ item }) => <Text>{item.title}</Text>}
keyExtractor={(item) => item.id}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
padding: 20,
},
});
Here is Expo Snack for testing - https://snack.expo.dev/#emmbyiringiro/a98de6
Note: Use Android or iOS emulator, not Web preview.

how to refresh the list after submitting a form in react-native?

when redirecting to index screen after submitting a post-form, the index screen does not show the newly added item in the list, can anyone help?
here is my Customer.js page
export default function Customer({ navigation }) {
const [customers, setCustomers] = useState([]);
const [isLoading, setLoading] = useState(true);
const getCustomers = async () => {
try {
const response = await fetch("http://localhost:3001/api/customers");
const json = await response.json();
setCustomers(json);
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
};
useEffect(() => {
getCustomers();
}, []);
return (
<View style={styles.item}>
<TouchableOpacity
onPress={() => navigation.navigate("AddCustomer")}
style={styles.btn}
>
<Text style={styles.btnText}>Add New Customer</Text>
</TouchableOpacity>
<FlatList
data={customers}
extraData={customers}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => navigation.navigate("CustomerDetails", item)}
>
<Text style={styles.item}>{item.name}</Text>
</TouchableOpacity>
)}
keyExtractor={(item) => item._id}
/>
</View>
);
}
}
and here is my AddCustomer.js page
const AddCustomer = ({ navigation, route }) => {
const [name, setName] = useState("");
const [phone, setPhone] = useState(0);
const [isGold, setIsGold] = useState(false);
const handleSubmit = async () => {
// e.preventDefault();
return await fetch("http://localhost:3001/api/customers", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: name,
phone: phone,
isGold: isGold,
}),
}).then(() => {
navigation.navigate("Customer", { customers: [name, phone, isGold] });
});
};
return (
<View>
<Text style={styles.title}>Add New Customer</Text>
<View>
<TextInput
style={styles.input}
onChangeText={(val) => setName(val)}
value={name}
placeholder="Your name"
onBlur={Keyboard.dismiss}
/>
<TextInput
style={styles.input}
onChangeText={(val) => setPhone(val)}
value={phone}
placeholder="phone number"
/>
<TextInput
style={styles.input}
onChangeText={(val) => setIsGold(val)}
value={isGold}
placeholder="is gold member"
autoCorrect={false}
autoCapitalize={false}
/>
</View>
<View style={styles.inputContainer}>
<TouchableOpacity style={styles.saveButton} onPress={handleSubmit}>
<Text style={styles.saveButtonText}>Add Customer</Text>
</TouchableOpacity>
</View>
</View>
);
};
new customer would be added and everything else work fine but the Customer page does not get re-rendered or refresh or reload.
In your Customer.js do it like below -
useEffect(() => {
const unsubscribe = navigation.addListener('focus', async () => {
getCustomers();
});
return unsubscribe ;
}, [navigation]);
import { useFocusEffect } from '#react-navigation/native';
useFocusEffect(
React.useCallback(() => {
// Do something when the screen is focused
getCustomers();
return () => {
// Do something when the screen is unfocused
};
}, []),
);

React-Native FlatList item clickable with data to another screen

I'm trying to access a screen when you click on an item in my flatlist by passing the date I retrieved from the firebase before, I've tried several things without success so I come to you.
Basically when I click on one of the elements -> A screen with details should appear.
export default function Notifications() {
const dbh = firebase.firestore();
const [loading, setLoading] = useState(true); // Set loading to true on component mount
const [deliveries, setDeliveries] = useState([]); // Initial empty array of users
useEffect(() => {
const subscriber = dbh
.collection("deliveries")
.onSnapshot((querySnapshot) => {
const deliveries = [];
querySnapshot.forEach((documentSnapshot) => {
deliveries.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setDeliveries(deliveries);
setLoading(false);
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}, []);
if (loading) {
return <ActivityIndicator />;
}
return (
<FlatList
style={{ flex: 1 }}
data={deliveries}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => { * HERE I NEED TO PASS DATA AND SHOW AN ANOTHER SCREEN FOR DETAILS * }}>
<View style={styles.container}>
<Text>DATE: {item.when}</Text>
<Text>ZIP DONATEUR: {item.zip_donator}</Text>
<Text>ZIP BENEFICIAIRE: {item.zip_tob_deliv}</Text>
</View>
</TouchableOpacity>
)}
/>
);
}
EDIT: Small precision this screen is located in a Tab.Navigator
you can pass params in navigation,
export default function Notifications(props) {
const { navigation } = props
const dbh = firebase.firestore();
const [loading, setLoading] = useState(true); // Set loading to true on component mount
const [deliveries, setDeliveries] = useState([]); // Initial empty array of users
useEffect(() => {
const subscriber = dbh
.collection("deliveries")
.onSnapshot((querySnapshot) => {
const deliveries = [];
querySnapshot.forEach((documentSnapshot) => {
deliveries.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setDeliveries(deliveries);
setLoading(false);
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}, []);
if (loading) {
return <ActivityIndicator />;
}
return (
<FlatList
style={{ flex: 1 }}
data={deliveries}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => {
navigation.navigate('screenName', {
//pass params here
})
}}>
<View style={styles.container}>
<Text>DATE: {item.when}</Text>
<Text>ZIP DONATEUR: {item.zip_donator}</Text>
<Text>ZIP BENEFICIAIRE: {item.zip_tob_deliv}</Text>
</View>
</TouchableOpacity>
)}
/>
);
}
you can access params in the navigated screen by props.route.params

How to display array elements in react native using FlatList

I'm trying to fetch data from an API and display that in a FlatList component in react. But when displaying data in the array, it displays an empty screen. Below is my code
useEffect(() => {
axios.get(`https://api.github.com/users/${user}/repos`)
.then(response => {
console.log(response.data)
response.data.map(repo => {
let rep = {
id:repo.id,
name:repo.name,
created:repo.created_at,
url:repo.html_url
}
repos.push(rep)
})
console.log(repos)
})
.catch(err => {
setError(true)
console.log(err)
})
},[])
return (
<View>
<Header navigate={navigation}/>
<FlatList
contentContainerStyle={{backgroundColor:'grey',marginTop:25}}
data={repos}
renderItem={({repo}) => (
<Text>
{repo.name}
</Text>
)}
/>
</View>
)
Here is the full working example: Expo Snack
import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, FlatList } from 'react-native';
import Constants from 'expo-constants';
import axios from 'axios';
export default function App() {
const [repos, setRepos] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
axios
.get(`https://api.github.com/users/theketan2/repos`)
.then((response) => {
let data = [];
//console.log(response.data);
response.data.map((repo) => {
let rep = {
id: repo?.id,
name: repo?.name,
created: repo?.created_at,
url: repo?.html_url,
};
data.push(rep);
});
// console.log(data);
setRepos(data);
})
.catch((err) => {
setError(true);
console.log(err);
});
}, []);
useEffect(() => {
console.log(repos);
}, [repos]);
return (
<View style={styles.container}>
<FlatList
data={repos}
renderItem={({item}) => (
<View style={styles.list}>
<Text>{item?.name}</Text>
</View>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'rgba(21,21,21,0.1)',
},
list: {
padding: 10,
marginTop: 5,
borderRadius: 5,
backgroundColor: 'white',
marginHorizontal: 5,
},
});
Try this way
const [data, setData] = useState([]);
useEffect(() => {
axios.get(`https://api.github.com/users/${user}/repos`)
.then(response => {
setData(response.data);
})
.catch(err => {
setError(true)
console.log(err)
})
},[])
return (
<View>
<Header navigate={navigation}/>
<FlatList
data={data}
renderItem={(item) => (
<Text>
{item.name}
</Text>
)}
/>
</View>
)

How to update state react native hooks from other screen using react navigation hook param?

How to update state react native hooks from other screen using react navigation hook param?
I am trying to update state selectedHotel in screen 1 from screen 2 that provide the data, so i save data from screen 2 in parameter using react navigation hooks params, the data is update but i can't update state selectHotel if data is exist in useEffect screen 1, here the code:
screen 1:
import {useNavigation, useNavigationParam} from 'react-navigation-hooks';
const TransportScreen = () => {
const hotelParam = useNavigationParam('hotel');
const [baseLoading, setBaseLoading] = useState(true);
const [selectedHotel, setSelectedHotel] = useState(
hotelParam ? hotelParam.id : '',
);
const {navigate} = useNavigation();
useEffect(() => {
setTimeout(() => {
setBaseLoading(false);
}, 1000);
if (hotelParam) {
setSelectedHotel(hotelParam.id);
console.log('update selected hotel', selectedHotel);
}
}, []);
const redirectTransportSelectHotel = () => {
console.log('select hotel');
navigate('TransportSelectHotel');
};
const submitTransport = () => {
console.log('getHotelId ', selectedHotel);
};
const renderContent = () => {
console.log('hotelId: ', selectedHotel);
if (!baseLoading) {
return (
<View
style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-start',
}}>
<MenuCard
expandRight
onPress={() => redirectTransportSelectHotel()}>
{hotelParam ? hotelParam.name : 'Select Hotel'}
</MenuCard>
<View
style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-end',
}}>
<View
style={{
flexDirection: 'row',
marginHorizontal: 40,
marginVertical: 20,
}}>
<Button onPress={() => submitTransport()}>Submit</Button>
</View>
</View>
</View>
);
}
return <LoaderScreen visible={baseLoading} />;
};
};
screen 2:
import {useNavigation, useNavigationParam} from 'react-navigation-hooks';
import {useSelector, useDispatch} from 'react-redux';
import {getHotels} from './actions';
import _ from 'lodash';
const TransportSelectHotelScreen = () => {
const {navigate} = useNavigation();
const dispatch = useDispatch();
const [baseLoading, setBaseLoading] = useState(true);
const {hotel} = useSelector(state => ({
hotel: state.transportHotelReducer,
}));
useEffect(() => {
setTimeout(() => {
setBaseLoading(false);
}, 1000);
loadHotels();
}, [loadHotels]);
const handleRefresh = () => {
console.log('refresh');
loadHotels();
};
const loadHotels = async () => {
dispatch(getHotels());
};
const redirectTransportCallback = hotel => {
console.log('hotel detail', hotel);
navigate('Transport', {hotel: hotel});
};
const renderItem = item => {
return (
<MenuCard
expandRight
onPress={() => redirectTransportCallback(item.item)}>
{item.item.name}
</MenuCard>
);
};
const renderContent = () => {
if (!baseLoading) {
if (!hotel.hotels.baseLoading) {
if (!_.isEmpty(hotel.hotels)) {
return (
<View style={globalStyles.menuContainer}>
<FlatList
data={hotel.hotels}
renderItem={renderItem}
keyExtractor={(item, index) => index.toString()}
refreshing={hotel.isRefreshing}
onRefresh={handleRefresh}
// onEndReached={handleLoadMore}
// onEndReachedThreshold={0.1}
/>
</View>
);
} else {
return (
<View style={globalStyles.wrapperContent}>
<Text>{Lang.no_data}</Text>
</View>
);
}
}
return <LoaderScreen visible={hotel.baseLoading} />;
}
return <LoaderScreen visible={baseLoading} />;
};
};
You could try
useEffect(() => {
setSelectedHotel(hotelParam ? hotelParam.id : '')
}, [hotelParam])