as I mentioned in the title, when I select a new value in my Picker, the console show that everything is ok but my fetch don't want to update it with the new selectedValue.
If I pass manually an argument into the useState, the fetch update it but I don't know how to force it to update automatically. Here the code :
export default function Bien() {
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
// problem here
const [selectedValue, setSelectedValue] = useState('tous');
const fetchValue = 'https://www.api.lereseaufoncier.fr/stock?category=' + selectedValue
// the console.log works and show the new selectedValue
console.log(fetchValue)
//but here, the query don't refresh
//if I pass 'maisons' or 'appartements' in the new state manually, it works
useEffect(() => {
fetch(fetchValue)
.then((response) => response.json())
.then((json) => setData(json ? json.table : []))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
const postLayoutComponent = data.map(table => <AEPostLayout key={table.ID} table={table}/> )
return (
<SafeAreaView>
<View style={{justifyContent: 'center', alignItems: 'center', width:'100%'}}>
<Spacer taille={30} />
<Picker
selectedValue={selectedValue}
style={{ height: 50, width: 195 }}
onValueChange={(itemValue, itemIndex) => setSelectedValue(itemValue)}
>
<Picker.Item label="Tous Nos Biens" value="tous" />
<Picker.Item label="Nos Maisons" value="maisons" />
<Picker.Item label="Nos Terrains à bâtir" value="terrains à bâtir" />
<Picker.Item label="Nos Appartements" value="appartements" />
</Picker>
<Spacer taille={10} />
</View>
<ScrollView>
{postLayoutComponent}
</ScrollView>
</SafeAreaView>
);
}
I hope someone can help me.
Thank you
Try the following:
useEffect(() => {
const fetchValue = 'https://www.api.lereseaufoncier.fr/stock?category=' + selectedValue
fetch(fetchValue)
.then((response) => response.json())
.then((json) => setData(json ? json.table : []))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, [selectedValue]);
That way, adding the dependency to useEffect tells the hook that every time selectedValue has changed, the component should re-render and re-fetch the data.
Thank you all it works. And also thank you for the link to the doc.
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.
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);
});
};
How do I display results from the API call in code lower in the page?
The {data.payrollid} after Complete is not showing any value. ie: the text only shows 'Complete' with no value after it.
My returned JSON looks like this...
{"status_code":200,"payrollid":10,"message":"Success"}
When I console.log(data) I can see that the fetch worked and I can see my JSON array.
Below is my React Native code
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
useEffect(() => {
fetch('https://www.mywebsite.ca/api/test.php')
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
return (
<>
<View>
{isLoading ? <Text>Loading...</Text> : <Text>Complete {data.payrollid}</Text> }
</View>
<View style={styles.container}>
<Text>This is my new app.</Text>
<Text>Some text</Text>
<StatusBar style="auto" />
</View>
</>
);
Your code should look something like this:
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState({});
useEffect(() => {
fetch('https://www.mywebsite.ca/api/test.php')
.then((response) => response.json())
.then((data) => setData(data))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
return (
<>
<View>
{isLoading ? <Text>Loading...</Text> :
<Text>Complete{data.payrollid}.</Text>
}
</View>
<View style={styles.container}>
<Text>This is my new app.</Text>
<Text>Your Text</Text>
<StatusBar style="auto" />
</View>
</>
);
You need to save your data in your data state.
const [data, setData] = useState({});
useEffect(() => {
fetch('https://www.mywebsite.ca/api/test.php')
.then((response) => response.json())
.then((data) => setData(data))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
And since your getting an object switch your original state to an object.
i have a magazine style App, it has an index page which - is componente named HomeScreen - (with a list of articles) and a detail page - which is component named Details - (with the full article);
Over them i have a Home componente which is a Stack navigation for bot.
Home component
const Home = ({}) => {
return (
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} options={HeaderOption} />
<Stack.Screen
name="Details"
component={DetailsScreen}
options={HeaderOption}
/>
</Stack.Navigator>
);
};
export default Home;
then (same file) got the HomeScreen component which render (correctly) the list of articles, it uses useEffect to get them from an API
function HomeScreen({ navigation }) {
const [article, setArticles] = useState();
const [loadingArticle, setLoading] = useState(false);
useEffect(() => {
console.log('start Fetching');
fetchTitles();
}, []);
const fetchTitles = () => {
fetch('myURL', {
method: 'GET',
})
.then((response) => response.json())
.then((responseData) => {
setArticles(responseData);
})
.finally(() => setLoading(false));
};
const { state } = useContext(AuthContext);
return (
<View
style={{
flex: 1,
paddingTop: Constants.HEIGTH,
backgroundColor: Constants.MAIN_GREEN,
}}
>
<LogoHeader title="TITOLO APP" isHome={true} navigation={navigation} />
<View style={{ ...styles.container }}>
<FlatList
data={article}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() =>
navigation.navigate('Details', {
itemId: item.id,
})
}
>
<Card>
<Card.Content>
<HTML
html={item.title.rendered}
baseFontStyle={{ fontSize: 20, color: '#adadad' }}
/>
<Paragraph>
Published on {moment(item.date).format('LL')}
</Paragraph>
</Card.Content>
<Card.Cover source={{ uri: item.jetpack_featured_media_url }} />
<Card.Content>
<HTML
html={item.excerpt.rendered}
imagesMaxWidth={Dimensions.get('window').width}
/>
</Card.Content>
</Card>
</TouchableOpacity>
)}
/>
</View>
</View>
);
}
When i click on the article i correctly reach Details with the correctly passed id but the singleArticle is 'null', even if responseData is correct:
function DetailsScreen({ route, navigation }) {
const { state } = useContext(AuthContext);
const [singleArticle, setSingleArticle] = useState(null);
const [loadingArticle, setLoadingSingle] = useState(false);
useEffect(() => {
setLoadingSingle(true);
console.log(loadingArticle); //return false (???)
fetchSingle();
}, []);
const fetchSingle = () => {
console.log('get_single');
fetch(
'myURLgetsingle?' +
route.params.itemId,
{
method: 'GET',
}
)
.then((response) => response.json())
.then((responseData) => {
console.log('setting');
console.log(responseData); //return the values i need
setSingleArticle(responseData);
console.log(singleArticle); //return null
})
.finally(() => setLoadingSingle(false));
};
console.log(singleArticle.title.rendered);
return (
<View
style={{
flex: 1,
paddingTop: Constants.HEIGTH,
backgroundColor: Constants.MAIN_GREEN,
}}
>
<LogoHeader title="" isHome={false} navigation={navigation} />
<Card style={{ ...styles.container, padding: 10 }}>
<Card.Content>
<Title>{singleArticle.title.rendered}</Title>
<Paragraph></Paragraph>
</Card.Content>
</Card>
</View>
);
}
i think im missing some basic use of react native, any way i can fix it up?
Thank you
Ciao, this is totally normal with Hooks. When you call setSingleArticle(responseData); if you try to log singleArticle on next line is pretty much sure that you got a null value. This happens because setSingleArticle is async and, in Hooks, you don't have a callback like:
setSingleArticle(responseData, () => {
// here I'm sure that singleArticle was setted
// with Hooks this is impossible to do!
});
So the result is that in this line console.log(singleArticle.title.rendered); you should receive an error like "cannot get property title of null". Since your singleArticle value comes from a fetch, you could try to modify singleArticle default value like:
const [singleArticle, setSingleArticle] = useState({title: {rendered: ''}});
With this initalization console.log(singleArticle.title.rendered); does not fails. Same thing for this line <Title>{singleArticle.title.rendered}</Title>
EDIT
For a is-loading style you can do:
<Title>{singleArticle.title.rendered === '' ? "Loading..." : singleArticle.title.rendered}</Title>
I have a request pulling in from YouTube, to create a list of videos I want to display in a flatlist. I use the same approach across the application (calling WordPress, etc...), but when Im trying to achieve the same with the YouTube API (I've got the key setup etc..), it throws an error;
const Watch = ({typeOfProfile}) => {
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
let x = {foo: 11, bar: 42};
function playertype(val) {
return 'https://www.googleapis.com/youtube/v3/searchpart=snippet&channelId=UCa_6KiOjxm6dEC_mMRP5lGA&maxResults=20&order=date&type=video&key=xxxxx';
}
useEffect(() => {
fetch(playertype(typeOfProfile))
.then((response) => response.json())
.then((json) => {
x = setData(json)
})
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
const result = Object.keys(x).map(key => ({[key]: x[key]}));
return (
<View style={styles.body}>
<View style={styles.topscroll}>
{isLoading ? <ActivityIndicator/> : (
<FlatList
data={result}
horizontal={true}
keyExtractor={({ id }, index) => id}
renderItem={({ item }) => (
<View>
<Text>
{x.val}
</Text>
</View>
)}
/>
)}
</View>
</View>
);
};
Someone mentioned it could be an object being returned instead of an array, seems odd the json structure is the same as other requests I use this approach for.
I discovered that I had to add brackets on the data property of the FlatList. So instead of
data={data}
I had to change it too;
data={[data]}
Code now;
<FlatList
data={[data]}
horizontal={true}
keyExtractor={({ id }, index) => id}
renderItem={({ item, index }) => (
<View style={styles.container}>
<Image style={styles.imgyoutubeprev} source={{ uri: chkValue(item.items[0].snippet.thumbnails.high.url) }} />
</View>
)}
/>