How to pass selected data to another screen from Flatlist - react-native

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

Related

Fetching data and Displaying from one Page to Another page but here it takes some time to display i don't Know why?

Iam Having Two Screens A and B,
Iam fetching Data from A Screen and displaying it in B Screen
it takes some time to display why ?
Screen A
import { View, Text,StyleSheet,SafeAreaView,FlatList,ActivityIndicator,TextInput,Button,TouchableHighlight} from 'react-native';
import React,{useEffect,useState} from 'react';
import {useNavigation} from '#react-navigation/native';
const movieURL="https://api.lyrics.ovh/suggest/"
const Search = ({navigation}) => {
const [data,setData]=useState([]);
const [text,setText]=useState([]);
const getLyrics=()=>{
const {TextInputValue}=text;
fetch(`${movieURL}/${TextInputValue}`)
.then((response)=>response.json())
.then((json)=>setData(json.data))
console.log("inside the getlyrics");
}
return (
<SafeAreaView>
<View>
<Text>Lyrics</Text>
<TextInput style={styles.input} onChangeText={TextInputValue=>setText({TextInputValue})}></TextInput>
<Button title="Search" onPress={()=>getLyrics()}></Button>
</View>
<FlatList
data={data}
keyExtractor={(item, index) => {
return item.id;
}}
renderItem={({ item,index }) => (
<View style={{ paddingBottom: 10 }}>
<Text style={styles.movieText}>
{item.artist.name}
{item.title}
<Button style={styles.lybtn} onPress{()=>navigation.navigate('LyricsGet',item)} title="Lyrics Get"></Button>
</Text>
</View>
)}
/>
</SafeAreaView>
)
}
Screen B
`import { View, Text,FlatList,Button,TouchableOpacity,StyleSheet,TextInput,RefreshControl} from 'react-native'
import React,{useState,useEffect} from 'react'
import { useIsFocused } from "#react-navigation/native";
const LyricsGet = ({route,navigation}) => {
const isFocused = useIsFocused();
const {title}=route.params;
console.log("the title of ",title);
const url="https://api.lyrics.ovh/v1/Sia";
const [song,setSong]=useState([]);
const [lyric,setLyric]=useState([]);
const [input,setInput]=useState([]);
const [isFetching, setIsFetching] = useState(false);
const [loading,setLoading]=useState(true)
const fetchData=()=>{
fetch(`${url}/${title}`)
.then(response => response.json())
.then(results => {
setLyric([results]);
setIsFetching(false);
setLoading(false);
}).catch(err=>{
Alert.alert("Something went Wrong");
})
}
useEffect(() => {
if(isFocused){
fetchData();
}
},[isFocused]);
return (
<View>
<FlatList
data={lyric}
renderItem={({ item,index }) => <Text key={item._id}>{item.lyrics}</Text>}
onRefresh={()=>fetchData()}
refreshing={loading}
/>
</View>
);
}
`
Iam Created Two Pages Screen A and Screen B.In Screen A iam having the Search box and whenever iam typing in that search box it fetches an data and displays it,from that iam clicking one data it redirects to another Page(Screen B) it shows some data.And Again iam go to Screen A and search something means it redirect to Screen B correctly but the old data shows for 50 seconds again and again it shows new data why? Please help me to find out this
`

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 Call Our Own Build withNavigation In React Native 6

Help,
Firstly, did React Native 6 or 5 have withNavigation ? I cannot find it in the documentation in React Native website.
So, I found some thread that we can build our own withNavigation like this :
withNavigation.js
import React from 'react';
import { useNavigation } from '#react-navigation/native'; // not sure package name
export const withNavigation = (Component) => {
return (props) => {
const navigation = useNavigation();
return <Component navigation={navigation} {...props} />;
};
};
But I don't know how to call or use the own build withNavigation.
This is my source code :
ResultList.js
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { FlatList, TouchableOpacity } from 'react-native-gesture-handler';
import ResultsDetail from './ResultsDetail';
import { withNavigation } from '../helper/withNavigation';
const ResultList = ({ title, results }) => {
console.log(withNavigation.component);
return (
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
<FlatList
horizontal={true}
showsHorizontalScrollIndicator={false}
data={results}
keyExtractor={(results) => results.id}
renderItem={({item}) => {
return (
<TouchableOpacity onPress={() => withNavigation('ResultShowScreen')}>
<ResultsDetail results={item} />
</TouchableOpacity>
)
}}
/>
</View>
)
};
const styles = StyleSheet.create({
title: {
fontSize: 18,
fontWeight: 'bold',
marginLeft: 15,
marginBottom: 5,
},
container: {
marginBottom: 10
}
});
export default ResultList;
In here :
console.log(withNavigation.component)
it show : undefined
How to get the navigation props with our own withNavigation ?
Thank You
Since your helper function, withNavigation, is a Higher-Order Component (HoC) you have to wrap your component with it in order to be able to use it:
const ResultList = ({ title, results, navigation }) => {
return (
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
<FlatList
horizontal={true}
showsHorizontalScrollIndicator={false}
data={results}
keyExtractor={(results) => results.id}
renderItem={({ item }) => {
return (
// Now you can use the navigation prop inside of your component
<TouchableOpacity onPress={() => navigation.navigate('MyRoute')}>
<ResultsDetail results={item} />
</TouchableOpacity>
);
}}
/>
</View>
);
};
// withNavigation adds the navigation property to your ResultList component's properties
export default withNavigation(ResultList);
I think the reason why you can't find withNavigation inside of React Navigation 5 or 6 anymore is because they suggest you to use their built-in hooks instead. I am not sure why do you need to use a custom made HoC when you could just use the useNavigation hook. If you don't have a specific reason to use your custom HoC, the built-in hooks could simplify your code:
import { useNavigation } from '#react-navigation/native';
const ResultList = ({ title, results }) => {
const navigation = useNavigation();
return (
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
<FlatList
horizontal={true}
showsHorizontalScrollIndicator={false}
data={results}
keyExtractor={(results) => results.id}
renderItem={({ item }) => {
return (
// You can use the navigation property inside of your component
<TouchableOpacity onPress={() => navigation.navigate('MyRoute')}>
<ResultsDetail results={item} />
</TouchableOpacity>
);
}}
/>
</View>
);
};
export default ResultList;
As per the upgrading documentation here
React Navigation 4.x included higher order components such as
withNavigation and withNavigationFocus. Now they live in the compat
package.
The documentation says that in order to upgrade from React Navigation 4 to 5:
withNavigation HOC is moved to another package #react-navigation/compat
So, in order to use that you have to import that package first:
import { withNavigation } from '#react-navigation/compat';

How to convert Fetch to Axios and Class component to Functional component?

How to convert Fetch to Axios and Class component to Functional component?
I want to learn to implement Infinite-Scrolling using functional component and axios in React native, but it is difficult to apply because the reference document is composed of class component and fetch.
import React from 'react';
import {
View,
Image,
Text,
FlatList, // here
} from 'react-native';
export default class App extends React.Component {
state = {
data: [],
page: 1 // here
}
_renderItem = ({item}) => (
<View style={{borderBottomWidth:1, marginTop: 20}}>
<Image source={{ uri: item.url }} style={{ height: 200}} />
<Text>{item.title}</Text>
<Text>{item.id}</Text>
</View>
);
_getData = () => {
const url = 'https://jsonplaceholder.typicode.com/photos?_limit=10&_page=' + this.state.page;
fetch(url)
.then(r => r.json())
.then(data => {
this.setState({
data: this.state.data.concat(data),
page: this.state.page + 1
})
});
}
componentDidMount() {
this._getData();
}
// here
_handleLoadMore = () => {
this._getData();
}
render() {
return (
<FlatList
data={this.state.data}
renderItem={this._renderItem}
keyExtractor={(item, index) => item.id}
onEndReached={this._handleLoadMore}
onEndReachedThreshold={1}
/>
);
}
}
When converting from a class to a function component, there are a few steps which are relevant here:
replace lifecycle events like componentDidMount with useEffect.
replace component state with one or many useState hooks.
convert class methods to plain functions.
remove all references to this.
delete render() and just return the JSX directly.
The methods _renderItem, _getData, and _handleLoadMore are basically unchanged. They just become const variables instead of class properties.
Here's the straight conversion from class to function component:
import React, { useEffect, useState } from 'react';
import {
View,
Image,
Text,
FlatList,
} from 'react-native';
export default function App() {
const [page, setPage] = useState(1);
const [data, setData] = useState([]);
const _renderItem = ({ item }) => (
<View style={{ borderBottomWidth: 1, marginTop: 20 }}>
<Image source={{ uri: item.url }} style={{ height: 200 }} />
<Text>{item.title}</Text>
<Text>{item.id}</Text>
</View>
);
const _getData = () => {
const url =
'https://jsonplaceholder.typicode.com/photos?_limit=10&_page=' + page;
fetch(url)
.then((r) => r.json())
.then((data) => {
setData(data.concat(data));
setPage(page + 1);
});
};
const _handleLoadMore = () => {
_getData();
};
// useEffect with an empty dependency array replaces componentDidMount()
useEffect(() => _getData(), []);
return (
<FlatList
data={data}
renderItem={_renderItem}
keyExtractor={(item, index) => item.id}
onEndReached={_handleLoadMore}
onEndReachedThreshold={1}
/>
);
}
Here it is with axios and with a few other improvements. I noticed that the end reached function was being called upon reaching the end of the initial zero-length list causing the first page to be fetched twice. So actually the componentDidMount is not needed. I changed from .then() to async/await, but that doesn't matter.
import React, { useEffect, useState } from 'react';
import { View, Image, Text, FlatList } from 'react-native';
import axios from 'axios';
export default function App() {
const [page, setPage] = useState(1);
const [data, setData] = useState([]);
const _renderItem = ({ item }) => (
<View style={{ borderBottomWidth: 1, marginTop: 20 }}>
<Image source={{ uri: item.url }} style={{ height: 200 }} />
<Text>{item.title}</Text>
<Text>{item.id}</Text>
</View>
);
const _getData = async () => {
const url =
'https://jsonplaceholder.typicode.com/photos?_limit=10&_page=' + page;
const res = await axios.get(url);
setData(data.concat(res.data));
setPage(page + 1);
};
const _handleLoadMore = () => {
_getData();
};
// useEffect with an empty dependency array replaces componentDidMount()
useEffect(() => {
// put async functions inside curly braces to that you aren't returing the Promise
_getData();
}, []);
return (
<FlatList
data={data}
renderItem={_renderItem}
keyExtractor={(item, index) => item.id}
onEndReached={_handleLoadMore}
onEndReachedThreshold={1}
/>
);
}
Expo Link -- It works on my device, but the infinite scroll doesn't seem to work in the web preview.
There are additional more "advanced" improvements that you can make:
set state with a callback of previous state to ensure that values are always correct. setPage(current => current + 1) setData(current => current.concat(res.data))
memoization with useCallback so that functions like _renderItem maintain a constant reference across re-renders.
exhaustive useEffect dependencies (requires memoization).

How to call the redux actions of one component in another

I'm rendering the data got from an API into the Cards, I created a CardContainers component that map the data I get from the API then use that component in another component.
CardContainers.js
import React from 'react';
import {View} from 'react-native';
import {withNavigation} from 'react-navigation';
class CardContainers extends React.Component{
addPlace(){
return this.props.addPlace;
}
renderCards(){
return this.props.data.map((item, index) => {
return (
<View key={index}>
{this.props.renderCard(item)}
</View>
)
})
}
render(){
return(
<View>{this.renderCards()}</View>
)
}
}
PlacesList.js
import React from 'react';
import ProgressiveInput from 'react-native-progressive-input';
import {StyleSheet, View, Alert, Text, TouchableOpacity, ListView, ScrollView} from 'react-native';
import {Button, Card, Icon} from 'react-native-elements';
import {connect} from 'react-redux';
import CardContainers from './CardContainers';
import * as placesActions from '../redux/actions/placesActions';
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
});
class PlacesList extends React.Component{
static navigationOptions = ({navigation}) => {
return {
title:'Finding new places',
}
}
constructor(props){
super(props)
const position = this.props.navigation.getParam('position')
const tripId = JSON.stringify(this.props.navigation.getParam('tripId')).replace(/\"/g,"");
const date = JSON.stringify(this.props.navigation.getParam('date')).replace(/\"/g,"");
this.state={
position: position,
tripId: tripId,
date: date,
dataSource:ds.cloneWithRows([]),
}
}
renderCard(item){
return(
<Card
key={item.id}
title={item.title}
titleStyle={styles.title}
containerStyle={{marginTop: 20, marginBottom:20}}
>
<View style={{flexDirection: 'row'}}>
<Text style={{margin:10}}>
Category: {item.category.title}
</Text>
<Text style={{margin:10}}>
Rating: {item.averageRating}
</Text>
</View>
<View style ={{flexDirection:'row', alignItems:'center', alignSelf:'center'}}>
<Button
icon={<Icon name='add' color='#ffffff'/>}
buttonStyle={{marginLeft:15, borderRadius:10}}
title='ADD THIS PLACE'
type='solid'
onPress={()=>this.props.addPlace()}
/>
</View>
</Card>
);
}
getPlacesAroundDestination = () => {
this.props.aroundPlaces(this.state.position);
this.setState({dataSource: ds.cloneWithRows(this.props.placesAround)})
}
autoComplete = async(query) => {
this.setState({destination: query})
await this.props.suggestPlaces(this.state.position, query)
this.setState({dataSource: ds.cloneWithRows(this.props.searchSuggests)})
}
inputCleared = () => {
this.setState({
destination:'',
isLoading: false,
dataSource: ds.cloneWithRows({}),
});
}
onListItemClicked = (searchSuggests) => {
this.setState({
title: searchSuggests.title,
placeId: searchSuggests.id,
openingHours: searchSuggests.openingHours,
category: searchSuggests.category,
position:searchSuggests.position.toString(),
dataSource:ds.cloneWithRows([]),
})
}
renderRow = (searchSuggests) => {
return(
<TouchableOpacity
style={{padding:10}}
onPress={()=>this.onListItemClicked(searchSuggests)}
>
<Text style={{fontSize:20}}>{searchSuggests.title}</Text>
<Text style={{fontSize:10}}>{searchSuggests.vicinity}</Text>
</TouchableOpacity>
)
}
renderSeparator = () => {
return <View style={{borderWidth:0.5, borderColor:'lightgrey',}}> </View>
}
renderContent(){
return (
<CardContainers
data={this.props.placesAround.items}
renderCard={this.renderCard}
/>
)
}
render(){
return(
<View style={styles.container}>
<ProgressiveInput
style={{marginTop:20, marginLeft:10, marginRight:10}}
placeholder='Your destination...'
value={this.state.destination}
isLoading={this.props.isLoading}
onChangeText={this.autoComplete}
onInputCleared={this.inputCleared}
/>
<View style={{flex:0}}>
<ListView
enableEmptySections
style={{backgroundColor:'white', margin:20}}
dataSource={this.state.dataSource}
renderRow={this.renderRow}
renderSeparator={this.renderSeparator}
/>
</View>
<Button
title= 'SUGGEST'
style={{alignSelf:'center'}}
onPress={() => this.props.aroundPlaces(this.state.position)}
/>
<ScrollView>
{this.props.placesAround.items? this.renderContent():null}
</ScrollView>
</View>
)
}
}
const mapStateToProps = (state) => {
return{
searchSuggests: state.places.searchSuggests,
isLoading: state.places.isLoading,
placesAround: state.places.placesAround,
geolo: state.location.latitude + ',' + state.location.longitude,
}
}
const mapDispatchToProps = (dispatch) => {
return {
addPlace:(tripId, date, title, category, rating, placeID) => dispatch (placesOfPlanActions.addPlace(tripId, date, title, category, rating, placeID)),
aroundPlaces: (geolo) => dispatch(placesActions.aroundPlaces(geolo)),
suggestPlaces: (geolo, destination) => dispatch(placesActions.suggestPlaces(geolo, destination))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(PlacesList);
As you can see in the code below, I want to call the addPlace() function which is a redux action in the onPress event of each Card rendered, but I cannot do it because the CardContainers does not have that function inside. So is there any way that I can do it? I'm quite new to react-native and redux, just spent 4 months on this and I do not think that I fully understand it.
Yup, simply add the connect HOC to CardContainers and you should be able to set the function in mapDispatchToProps like you did in PlacesList.