I have a function to fetch items from an API that is inside UseEffect. And i'm looking to call this function every time the status of the selectedItem or the items changes and show an ActivityIndicator before the function returns the result. The ActivityIndicator appears when the items are uploading but not when the status of the selectedItem changes ?
I have my code like this :
export default () => {
const [items, setItems] = useState();
const [selectedItem, setSelectedItem] = useState(null);
const [isLoading, setLoading] = useState(true);
const getItems = () => {
get('/api/items').then((rep) => {
setItems(rep);
setLoading(false);
}
});
};
useEffect(() => {
getItems();
}, [selectedItem.status]);
return (
<SafeAreaView style={styles.container}>
{isLoading ? (
<View style={[styles.spinnerContainer, styles.horizontal]}>
<ActivityIndicator />
</View>
) : ((items !== [])
&& (
<SectionList
stickySectionHeadersEnabled={false}
style={{ paddingHorizontal: 20, }}
sections={items}
refreshing={isLoading}
keyExtractor={(item, index) => item + index}
...
/>
))}
</SafeAreaView>
);
};
You can try setLoading(true) inside getItems
const getItems = () => {
setLoading(true);
get('/api/items').then((rep) => {
setItems(rep);
setLoading(false);
});
};
Related
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.
Does anyone know how I can change the state of a renderItem when it leaves screen? Below I have the Flatlist with uses an Item, I want to change the state of the item once it exits the renderview.
const Item = memo(({content}) => {
const [outOfView, setOutOfView] = useState(false)
const onScroll= () => {
if (!outOfView) setOutOfView(true) //Trying to get this to work
}
return (
<View style={styles.item} onScroll={onScroll}>
<Text>{content.title}</Text>
</View>
)
})
const Slider = props => {
const flatList = useRef()
const _renderItem = ({ item, index }) => <Item content={item} />
return (
<View style={styles.container} >
{props.header ? <AppText style={styles.header} text={props.header} /> : null}
<FlatList
data={props.data}
horizontal
pagingEnabled
renderItem={_renderItem}
keyExtractor={item => item._id}
ref={flatList}
/>
</View>
)
}
YOu can do something like this
import { useIsFocused } from '#react-navigation/native';
const Item = memo(({content}) => {
const [outOfView, setOutOfView] = useState(false)
const onScroll= () => {
if (!outOfView) setOutOfView(true) //Trying to get this to work
}
const isFocused = useIsFocused();
return (
<View style={styles.item} onScroll={onScroll}>
<Text>{isFocused?content.title:"Offline"}</Text>
</View>
)
})
hope it helps. feel free for doubts
I'm making a mobile app that shows a list of movies, but when I search for a movie FlatList won't update, how can I fix it?
I tried too many things but it still does not work, my objective is to update the list when the button is pressed, the API gives me the data correctly but the list does not update.
This is my code:
export const Home = () => {
let { peliculasList, loadPeliculas } = peliculasPaginated();
const [name, setName] = useState('');
const [year, setYear] = useState('');
const [buscado, setBuscado] = useState(false);
const handleClick = async () => {
const resp = await peliculasApi.get<SimplePelicula[]>(`http://www.omdbapi.com/?t=${name}&y=${year}&plot=full&apikey=d713e8aa`);
setBuscado(!buscado);
peliculasList = resp.data
}
return (
<>
<View
style={{
alignItems: 'center',
height: 760
}}
>
<Text style={{
...style.title,
...style.globalMargin,
top: 0,
marginBottom: 0
}}>Movies</Text>
<TextInput
placeholder='Movie Name'
style={styles.input}
onChangeText={(val) => setName(val)}
/>
<TextInput
placeholder='Year'
style={styles.inputMovie}
onChangeText={(val) => setYear(val)}
/>
<TouchableOpacity onPress={() => handleClick()}>
<ButtonSr></ButtonSr>
</TouchableOpacity>
<FlatList
data={ peliculasList }
keyExtractor={ (pelicula) => pelicula.imdbID }
showsVerticalScrollIndicator={ false }
extraData={ buscado }
renderItem={({ item }) => ( <PeliculasCard pelicula={item} ></PeliculasCard> )}
/>
</View>
</>
)
}
Try to save your resp.data within the state and declare that state in your Flatlist's data prop may solve the issue.
Try this out just change the 'MOVIENAME' to a response from the api such as the movie name and refrence it as the item.API object of your choice
import React, { useState } from 'react'
import { View, Text, TextInput, FlatList, Button } from 'react-native'
export default function App() {
const [FetchedData, setFetchedData] = useState([])
const [SearchTerm, setSearchTerm] = useState('')
const [Data, setData] = useState(FetchedData)
const [ArrayHolder, setArrayHolder] = useState(FetchedData)
const FetchMovies = () => {
fetch('url')
.then(res => res.json())
.then(res => setFetchedData(res))
}
FetchMovies()
function dataSearch(text) {
const newData = ArrayHolder.filter(item => {
const itemData = item.MOVIENAME.toUpperCase()
const textData = text.toUpperCase()
return itemData.indexOf(textData) > -1
})
setData(newData)
}
return (
<View>
<Button title='Press' onPress={() => dataSearch(SearchTerm)} />
<TextInput
onChangeText={(text) => setSearchTerm(text)}
placeholder="Search Here"
/>
<FlatList
data={Data}
renderItem={({ item }) => <Text>{item.MOVIENAME}</Text>}
/>
</View>
)
}
im trying to get repos in order of stars from the API, but as i scroll, i keep getting random duplicates :
export default function App() {
const [repos, setRepos] = useState([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(true);
useEffect(() => {
const getUsers = async () => {
const res = await axios.get(
`https://api.github.com/search/repositories?q=created:>2020-01-01&sort=stars&order=desc&page=${page}`
);
const data = await res.data;
// setRepos([...repos, ...data.items]);
setRepos([...repos, ...data.items]);
setLoading(false);
};
getUsers();
}, [page]);
const scrollToEnd = () => {
if (loading === false) {
setPage(page + 1);
setLoading(true);
}
console.log("page", page);
};
const renderItem = ({ item }) => (
<Text className="boxed">
Stars: {item.stargazers_count} Id: {item.id}
</Text>
);
return (
<SafeAreaView style={styles.screen}>
<View style={styles.container}>
<FlatList
data={repos}
renderItem={renderItem}
keyExtractor={(item) => item.id}
onEndReached={scrollToEnd}
showsVerticalScrollIndicator={false}
/>
{loading && <Text className="loading">...loading</Text>}
</View>
<StatusBar style="auto" />
</SafeAreaView>
);
}
You should avoid duplicate data before set it to state, update your getUsers function with this,
useEffect(() => {
const getUsers = async () => {
const res = await Axios.get(
`https://api.github.com/search/repositories?q=created:>2020-01-01&sort=stars&order=desc&page=${page}`,
);
const data = await res.data;
let arrRepos = [...repos, ...data.items];
let uniq = new Set(arrRepos.map((objRepo) => JSON.stringify(objRepo)));
arrRepos = Array.from(uniq).map((objRepo) => JSON.parse(objRepo));
setRepos(arrRepos);
setLoading(false);
};
getUsers();
}, [page]);
I think you got too much going on in your useEffect function, maybe missing a closing }. Try this:
function App() {
const [repos, setRepos] = useState([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(true);
// This will trigger when page changes:
useEffect(() => {
getUsers();
}, [page]);
const getUsers = async () => {
const res = await axios.get(
`https://api.github.com/search/repositories?q=created:>2020-01-01&sort=stars&order=desc&page=${page}`
);
const data = await res.data;
// setRepos([...repos, ...data.items]);
setRepos([...repos, ...data.items]);
setLoading(false);
};
const scrollToEnd = () => {
if (loading === false) {
setPage(page + 1);
setLoading(true);
}
console.log("page", page);
};
const renderItem = ({ item }) => (
<Text className="boxed">
Stars: {item.stargazers_count} Id: {item.id}
</Text>
);
return (
<SafeAreaView style={styles.screen}>
<View style={styles.container}>
<FlatList
data={repos}
renderItem={renderItem}
keyExtractor={(item) => item.id}
onEndReached={scrollToEnd}
showsVerticalScrollIndicator={false}
/>
{loading && <Text className="loading">...loading</Text>}
</View>
<StatusBar style="auto" />
</SafeAreaView>
);
}
export default App;
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