how do i pass the data to the details screen? Am trying but it is giving me this error : invalid attempt to destructure non-iterable instance - react-native

Home Screen:
import React from 'react';
import {View,Text, Image,StyleSheet,TouchableOpacity} from 'react-native';
import {useNavigation} from '#react-navigation/native';
export default function Books({item}){
const firstUrl= 'https://carp-kenya.herokuapp.com';
var secondUrl= String(item.imageFile) ;
var urls= String (firstUrl+secondUrl);
const navigation = useNavigation();
const{Title,description,File}=item;
return (
<View style={styles.container1}>
<TouchableOpacity onPress={()=>navigation.navigate('DetailsScreen',{file:File,Description: description,Title:Title})} >
<Image style={styles.image} source={{uri: urls}}/>
<View style={styles.view2}>
<Text style={styles.title}> {Title}</Text>
</View>
</TouchableOpacity>
</View>
)
}
Source screen:-
import React,{useEffect,useState,useCallback} from 'react';
import {View, TouchableOpacity, Text, StyleSheet, SafeAreaView, FlatList, ActivityIndicator, Alert, ScrollView} from 'react-native';
import Books from '../resources/bookSource';
import {useNavigation} from '#react-navigation/native'
export default function RegistrationScreen({item}){
const [isLoading, setLoading] = useState(false);
const [user, setUser] = useState([]);
const [cat, setCat] = useState([]);
const [text,setText]=useState("");
const [ref, setRef]= useState([])
const [refreshing, setRefreshing] = useState(false);
const navigation = useNavigation();
const ActionNav =()=>{searchData('action')}
const DramaNav =()=>{searchData('drama')}
const InspirationalNav =()=>{searchData('inspirational')}
const EducationalNav =()=>{searchData('educational')}
useEffect(()=>{
fetchData()
},[])
const fetchData = () => {
setLoading(true);
fetch('https://carp-kenya.herokuapp.com/book_json_format/')
.then((response) => response.json())
.then((json) =>{setUser(json),
(setCat(json)),
(setRef(json)) })
.catch(() => Alert.alert('Something went wrong..', 'There was an error.'))
.finally(() => {
setLoading(false)
});
}
const searchData = (text) => {
const newData = cat.filter((item) => {
const itemData = item.category.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
setUser(newData);
setText(text);
}
const onRefresh = useCallback(() => {
setRefreshing(true);
fetch('https://carp-kenya.herokuapp.com/book_json_format/')
.then((response) => response.json())
.then((json) =>setUser(json))
.catch(() => Alert.alert('Something went wrong..', 'There was an error.'))
.finally(() => {
setRefreshing(false)
});
}, []);
return(
<SafeAreaView >
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
<View style={styles.container}>
<TouchableOpacity style={styles.category} onPress={InspirationalNav}>
<Text style={styles.text}>Inspirational</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.category} onPress={EducationalNav}>
<Text style={styles.text}>Educative</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.category} onPress={DramaNav} >
<Text style={styles.text}>Drama</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.category} onPress={ActionNav}>
<Text style={styles.text}>Action</Text>
</TouchableOpacity>
</View>
</ScrollView>
{isLoading ? <ActivityIndicator style={styles.containers}/> :
<FlatList
data={user}
showsHorizontalScrollIndicator={false}
keyExtractor={({ id }, index) => id}
renderItem={( {item }) => (<Books item={item} />)
}
numColumns={3}
onRefresh={()=>onRefresh()}
refreshing={refreshing}
/>
}
</SafeAreaView>
);

Invalid attempt to destructure non-iterable instance
says the instance you are trying to iterate is not iterable. What you should do is checking whether the opt object is iterable and can be accessed in the JSX code.
Check how you are passing params props to Books please check and log it

Related

How do I get setState to update a value immediately?

I have to press the buttons twice to update the filter value for how I want to display the movies in my app. This is my code:
const HomeScreen = () => {
const navigation = useNavigation();
const [movies, setMovies] = useState({});
useEffect(() => {
getMovies();
},[])
useEffect(() => {
getMoviesFiltered(filter);
},[filter])
const [filter, setFilter] = useState('name');
const getMovies = async (filter) =>{
const querySnapshot = await getDocs(query(collection(db, "movies"), orderBy(filter)));
setMovies(querySnapshot.docs);
}
return (
<View>
<View>
<TouchableOpacity onPress = {() => {setFilter('name'); getMovies(filter)}}>
<Text>Title</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('runtime'); getMovies(filter)}}>
<Text>Runtime</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('year'); getMovies(filter)}}>
<Text>Year</Text>
</TouchableOpacity>
</View>
</View>
<FlatList
data = {movies}
renderItem = {({item}) => (
<TouchableOpacity onPress={() => navigation.navigate('Modal', item.data())}>
<Image source={{uri: item.data().pic}}/>
</TouchableOpacity>
)
}
/>
</View>
)
}
export default HomeScreen
I know that setState is asynchronous and that that is the reason it happens, but I'm kind of stuck on not knowing how to change it properly, so I'd appreaciate the help. Thank you.
useEffect will run when the values within dependency array change.
So, you don't need to add function getMovies after you changed your filter. Just simply move that to useEffect.
const HomeScreen = () => {
const navigation = useNavigation();
const [movies, setMovies] = useState({});
const [filter, setFilter] = useState('name');
useEffect(() => {
//when page is initialize, run this
getMovies(filter);
},[])
useEffect(() => {
//when filter is changed, use latest value to run this
getMovies(filter);
},[filter]);
const getMovies = async (filter) =>{
const querySnapshot = await getDocs(query(collection(db, "movies"), orderBy(filter)));
setMovies(querySnapshot.docs);
}
return (
<View>
<View>
<TouchableOpacity onPress = {() => {setFilter('name');}}>
<Text>Title</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('runtime');}}>
<Text>Runtime</Text>
</TouchableOpacity>
<TouchableOpacity onPress = {() => {setFilter('year');}}>
<Text>Year</Text>
</TouchableOpacity>
</View>
<FlatList
data = {movies}
renderItem = {({item}) => (
<TouchableOpacity onPress={() => navigation.navigate('Modal', item.data())}>
<Image source={{uri: item.data().pic}}/>
</TouchableOpacity>
)
}
/>
</View>
)
}
export default HomeScreen

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.

Flatlist item redirects to details page

I am new at react native and I am trying to make a detail page for my crypto price API.
What I need to do is when the user press on crypto he is redirected to a detail screen page where he can see charts, price etc. I have no idea what I should do next, I tried onpress() but it did not work. How can I make those flatList elements that are displayed using CryptoList when clicking on them shows detail page?
App.js
export default function App() {
const [data, setData] = useState([]);
const [selectedCoinData, setSelectedCoinData] = useState(null);
useEffect(() => {
const fetchMarketData = async () => {
const marketData = await getMarketData();
setData(marketData);
}
fetchMarketData();
}, [])
return (
<View style={styles.container}>
<View style={styles.titleWrap}>
<Text style={styles.largeTitle}>
Crypto
</Text>
<Divider width={1} style={styles.divider} />
</View>
<FlatList
keyExtractor={(item) => item.id}
data={data}
renderItem={({ item }) => (
<CryptoList
name={item.name}
symbol={item.symbol}
currentPrice={item.current_price}
priceChangePercentage={item.price_change_percentage_24h}
logoUrl={item.image}
/>
)}
/>
</View>
);
}
cryptoList.js
const CryptoList = ({ name, symbol, currentPrice, priceChangePercentage, logoUrl}) => {
const priceChangeColor = priceChangePercentage > 0 ? 'green' : 'red';
return (
<TouchableOpacity>
<View style={styles.itemWrapper}>
{/*Left side view*/}
<View style={styles.leftWrap}>
<Image source={{uri: logoUrl}} style={styles.image}/>
<View style={styles.titleWrapper}>
<Text style={styles.title}>{ name }</Text>
<Text style={styles.subtitle}>{ symbol.toUpperCase() }</Text>
</View>
</View>
{/*Right side view*/}
<View style={styles.rightWrap}>
<Text style={styles.title}>€{currentPrice.toLocaleString('de-DE', {currency: 'Eur'})}</Text>
<Text style={[styles.subtitle,{color: priceChangeColor} ]}>{priceChangePercentage.toFixed(2)}%</Text>
</View>
</View>
</TouchableOpacity>
)
}
import React, { useEffect, useState } from "react";
import { Text, View, StyleSheet, FlatList} from 'react-native';
import { Divider, useTheme } from 'react-native-elements';
import Constants from 'expo-constants';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import { HomeScreen } from './pages/homeScreen';
// You can import from local files
import CryptoList from './components/cyproList';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
import { getMarketData } from './components/cryptoApi';
const Stack = createNativeStackNavigator();
export default function App() {
const [data, setData] = useState([]);
const [selectedCoinData, setSelectedCoinData] = useState(null);
useEffect(() => {
const fetchMarketData = async () => {
const marketData = await getMarketData();
setData(marketData);
}
fetchMarketData();
}, [])
return (
<View style={styles.container}>
<View style={styles.titleWrap}>
<Text style={styles.largeTitle}>
Kriptovalūtu cenas
</Text>
<Divider width={1} style={styles.divider} />
</View>
<FlatList
keyExtractor={(item) => item.id}
data={data}
renderItem={({ item }) => (
<CryptoList
name={item.name}
symbol={item.symbol}
currentPrice={item.current_price}
priceChangePercentage={item.price_change_percentage_24h}
logoUrl={item.image}
/>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container:{
flex: 1,
backgroundColor: '#fff',
},
titleWrap:{
marginTop:50,
paddingHorizontal: 15,
},
largeTitle:{
fontSize: 22,
fontWeight: 'bold',
},
divider: {
marginTop: 10,
}
});
<!-- begin snippet: js hide: false console: true babel: false -->
<div data-snack-id="2OtINTPVy" data-snack-platform="android" data-snack-preview="true" data-snack-theme="light" style="overflow:hidden;background:#F9F9F9;border:1px solid var(--color-border);border-radius:4px;height:505px;width:100%"></div>
<script async src="https://snack.expo.dev/embed.js"></script>
your code seems incomplete. Have you tried wonPress={() => navigate(DetailsPage, {selectedCoinData})} where selectedCoinData is the details of your coin, then on details page you can retrieve the info with the params of react navigation with const route = useRoute() and use route.params.selectedCoinData
<FlatList
keyExtractor={(item) => item.id}
data={data}
renderItem={({ item }) => (
<CryptoList
name={item.name}
symbol={item.symbol}
currentPrice={item.current_price}
onPress={() => navigate(DetailsPage, {selectedCoinData})}
priceChangePercentage={item.price_change_percentage_24h}
logoUrl={item.image}
/>
)}
/>
and then in your component
const CryptoList = ({ name, symbol, currentPrice, priceChangePercentage, logoUrl, onPress}) => { return (
<TouchableOpacity onPress={onPress}>

How to re-render a flatlist

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>
)
}

How to pass selected data to another screen from Flatlist

I am still new in using React Native and Mobile Apps Development. I tried to copy the code from another tutorial and have little bit of understanding it.
I have Save.js, Feed.js and Details.js. I have successfully retrieved the data from Save.js to Feed.js using FlatList and RenderItem. Now, I want to pass only selected data from Feed.js to Details.js. But I am confused which way to use, whether useNavigation, getParam, withNavigation or anything else? And is there any difference between using Hooks and Class? Btw I'm using Hooks.
Save.js
import { View, TextInput, Image, Button, StyleSheet, TouchableOpacity, Text} from 'react-native'
import { NavigationContainer } from '#react-navigation/native'
export default function Save(props, navigation) {
const [productName, setProductName] = useState("")
const [category, setCategory] = useState("")
return (
<View style={styles.inputView}>
<TextInput
placeholder="Product name..."
onChangeText={(productName) => setProductName(productName)}
/>
</View>
<View style={styles.inputView}>
<TextInput
placeholder="Category..."
onChangeText={(category) => setCategory(category)}
/>
</View>
Feed.js
function Feed(props, navigation) {
const { currentUser, posts } = props;
const { navigate } = useNavigation();
return (
<FlatList
data={posts}
keyExtractor={(item, index) => item.key}
contentContainerStyle={{
padding: 20,
paddingTop: StatusBar.currentHeight || 42,
}}
renderItem={({item, index}) => (
<TouchableOpacity
onPress={() => props.navigation.navigate("Details", {productName: item.productName})}
<View>
<Text>{item.productName}</Text>
<Text>Category : {item.category}</Text>
</View>
/>
)}
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts
})
export default connect(mapStateToProps, null)(Feed);
Details.js
export default function Details({ props, navigate, route }) {
const productName = props.navigation.route.params.productName;
const { navigate } = useNavigation();
const productName = useNavigationParam('productName');
return (
<View>
<Text>{productName}</Text>
<Text>{Category}</Text>
</View>
)
}
I am not sure which way to use in Details.js, so I just put all code I have used and tested.
the code bellow will help you and I think you have problem in destructing context this will help you. and remember navigation is an object inside props
Feed.js
function Feed(props) {
const { currentUser, posts, navigation } = props;
return (
<FlatList
data={posts}
keyExtractor={(item, index) => item.key}
contentContainerStyle={{
padding: 20,
paddingTop: StatusBar.currentHeight || 42,
}}
renderItem={({item, index}) => (
<TouchableOpacity
onPress={() => props.navigation.navigate("Details", {productName: item.productName})}
<View>
<Text>{item.productName}</Text>
<Text>Category : {item.category}</Text>
</View>
/>
)}
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts
})
export default connect(mapStateToProps, null)(Feed);
in Feed you dont need to use useNavigation() because props argument contain navigation.
Details.js
export default function Details(props) {
const {productName, category} = props.navigation.route.params;
return (
<TouchableOpacity onPress={()=>props.navigation.navigate("Save",{productName, category})}>
<Text>{productName}</Text>
<Text>{Category}</Text>
</TouchableOpacity>
)
}