React Native TouchableOpacity OnPress not Working with loop - reactjs

I have a scrollview in which multiple items are generated with loop. I added TouchableOpacity above these items because i want these objects to be touchable. But when i add a method on onPress method it shows error not a function , is undefined
List_Data Component:
class List_Data extends React.Component {
fetchData = () => {
console.log("DONE");
}
_renderView = () => {
return (
<View style={{flex:1, padding: 20}}>
<View style={styles.container}>
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false} >
{
this.state.Data.map(function (data, index) {
return (
<TouchableOpacity key={index} onPress={() => this.fetchData()}>
<Image source={{uri: data.imageSrc}}
resizeMode={'cover'}
style={{width: '100%', height: imageHeight}}
/>
</TouchableOpacity>
);
})
}
</ScrollView>
</View>
</View>
)
}
render() {
return (
{this._renderView()}
);
}
}
I don't know whats the issue, it just a method which prints on console.

The issue is coming from your .map. Basically you are losing the value of this as you are not using an arrow function. If you change your .map(function(data, index) to .map((data,index) => it should work.
import * as React from 'react';
import { Text, View, StyleSheet, ScrollView, TouchableOpacity, Image } from 'react-native';
import { Constants } from 'expo';
export default class App extends React.Component {
state = {
Data: [
{imageSrc :'https://randomuser.me/api/portraits/men/39.jpg'},
{imageSrc: 'https://randomuser.me/api/portraits/women/38.jpg'},
{imageSrc: 'https://randomuser.me/api/portraits/men/37.jpg'},
{imageSrc: 'https://randomuser.me/api/portraits/women/36.jpg'},
{imageSrc: 'https://randomuser.me/api/portraits/men/35.jpg'},
{imageSrc: 'https://randomuser.me/api/portraits/women/34.jpg'},
]
}
// let's pass something so that we know that it is working
fetchData = (index) => {
alert(`you pressed ${index}`)
}
_renderView = () => {
return (
<View style={{flex: 1, padding: 20}}>
<View style={styles.container}>
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false} >
{
this.state.Data.map((data, index) => { // change this to an arrow function
return (
<TouchableOpacity key={index} onPress={() => this.fetchData(index)}>
<Image source={{uri: data.imageSrc}}
resizeMode={'cover'}
style={{width: 100, height: 100}}
/>
</TouchableOpacity>
);
})
}
</ScrollView>
</View>
</View>
);
}
render() {
return (
this._renderView()
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
}
});
You can see it working in the following snack https://snack.expo.io/#andypandy/map-with-arrow-function

Try keyboardShouldPersistTaps={true} under ScrollView. <ScrollView keyboardShouldPersistTaps={true}>

Related

Preventing state item in all components from changing with useContext()?

I'm trying to control the state of 2 components use useContext() in React-Native. I have a flatmap of cards that render on a screen. Each card has a press-able 'interested' icon. If I click on a card, it shows the details of the card and also the press-able 'interested' icon. Both should share the same state of being pressed and also send a 'interested bool to the backend.
Right now, if I press 'interested' on one card, the all get pressed. Same with the inside icon of the details page. Obviously the 'interested' state in the Context Provider is to broad and changing all of them. How can I filter out the state change?
Here is the card component that gets rendered from a flatlist:
import React, { useState, useEffect, useContext } from 'react';
import { Image, View, Text, TouchableOpacity } from 'react-native';
import { localDate } from 'utils/formatDate';
import PropTypes from 'prop-types';
import { ConcertContext } from '../../../context/ConcertContextProvider';
import LocationIcon from '../../../assets/images/locationIcon.svg';
import InterestedFilledIcon from '../../../assets/images/interested-filled.svg';
import InterestedEmptyIcon from '../../../assets/images/interested-empty.svg';
import ShareIcon from '../../../assets/images/share.svg';
import styles from './styles';
export default function ConcertCard({
gotoConcertPage,
artist,
cover,
concertCity,
scheduledAt,
description,
concertObj,
}) {
const [clickInterest, setClickInterest] = useState(concertObj.is_interested);
const { interested, setInterested, concertInterest } = useContext(ConcertContext);
const handleInterest = () => {
setInterested(prevState => !prevState);
concertInterest(concertObj);
};
useEffect(() => {
setClickInterest(interested);
}, [interested]);
return (
<TouchableOpacity onPress={() => gotoConcertPage(concertObj)}>
<View style={styles.card}>
<View style={styles.cardContent}>
<View style={styles.cropped}>
<Image style={styles.image} source={{ url: cover.url }} />
</View>
<View style={styles.textWrapper}>
<Text style={styles.date}>{localDate(scheduledAt, 'MMM D, h:mm A z')}</Text>
<View style={styles.locationWrapper}>
<LocationIcon style={styles.locationIcon} />
<Text style={styles.location}>{concertCity.name}</Text>
<Text style={styles.location}>{concertCity.address}</Text>
</View>
<Text style={styles.name}>{artist.name}</Text>
<Text style={styles.description}>{description}</Text>
</View>
<View style={styles.border} />
</View>
<View style={styles.icons}>
<View style={styles.sharedIcon}>
<ShareIcon />
</View>
{!clickInterest ? (
<InterestedEmptyIcon onPress={handleInterest} />
) : (
<InterestedFilledIcon onPress={handleInterest} />
)}
</View>
</View>
</TouchableOpacity>
);
}
ConcertCard.propTypes = {
gotoConcertPage: PropTypes.func,
artist: PropTypes.object,
cover: PropTypes.object,
concertCity: PropTypes.object,
scheduledAt: PropTypes.string,
description: PropTypes.string,
concertObj: PropTypes.object,
};
Here is the card details page:
import React, { useState, useEffect, useContext } from 'react';
import { Text, View, Image, ImageBackground, ScrollView, FlatList } from 'react-native';
import PropTypes from 'prop-types';
import Button from 'components/button';
import { localDate } from 'utils/formatDate';
import { ConcertContext } from '../../context/ConcertContextProvider';
import LocationIcon from '../../assets/images/locationIconTwo.svg';
import LittleFriendIcon from '../../assets/images/little-friend.svg';
import Star from '../../assets/images/Star.svg';
import QuestionMarkIcon from '../../assets/images/question-mark.svg';
import ShareIcon from '../../assets/images/ShareIconLarge.svg';
import styles from './concertStyles';
export default function ConcertPageScreen({ navigation, route }) {
const {
concertObj,
name,
artist,
scheduled_at,
interests,
concert_city,
other_cities,
description,
ticket_base_price,
} = route.params;
const { interested, setInterested, concertInterest } = useContext(ConcertContext);
const formatLocation = city => {
return city.slice(0, city.length - 5);
};
const handleInterest = () => {
setInterested(prevState => !prevState);
concertInterest(concertObj);
};
const buyTicket = concertObj => {
console.log('Buy ticked: ', concertObj.id);
};
return (
<>
<View style={styles.wrapper}>
<ImageBackground
style={styles.imageBackground}
imageStyle={{ opacity: 0.3 }}
source={{ url: artist.media }}
/>
<ScrollView vertical>
<View style={styles.headerWrapper}>
<Text style={styles.headerTitle}>{name}</Text>
<Text style={styles.smallText}>{artist.name}</Text>
<Text style={styles.date}>{localDate(scheduled_at, 'MMM D, h:mm A z')}</Text>
<View style={styles.locationWrapper}>
<LocationIcon style={styles.locationIcon} />
<Text style={styles.location}>{concert_city.name}</Text>
<Text style={styles.location}>{concert_city.address}</Text>
</View>
<Text style={styles.description}>{description}</Text>
<View style={styles.interestedWrapper}>
<LittleFriendIcon style={styles.littleMan} />
<Text style={styles.interested}>{interests} Interested</Text>
</View>
<View
style={{
borderTopColor: '#DADADA',
borderTopWidth: 0.2,
borderStyle: 'solid',
alignSelf: 'center',
height: 5,
marginTop: 32,
marginBottom: 28.5,
}}
/>
<View style={styles.tableWrapper}>
<View style={styles.tableHeaderWrapper}>
<Text style={styles.headerTitle}>DATES</Text>
<Text style={styles.headerTitle}>LOCATIONS</Text>
</View>
<View>
<FlatList
data={other_cities}
initialNumToRender={1}
renderItem={({ item }) => (
<View style={styles.tableItemsWrapper}>
<Text style={styles.dateItem}>
{localDate(item.date, 'MMM DD - h:mm A z')}
</Text>
<Text style={styles.locationItem}>{formatLocation(item.city)}</Text>
</View>
)}
concertCityId={item => `item${item.concert_city_id}`}
concertScheduleId={item => `item${item.concert_schedule_id}`}
/>
</View>
</View>
<View style={styles.iconWrapper}>
<View style={{ backgroundColor: '#292929', width: 44, height: 44, borderRadius: 30 }}>
<ShareIcon style={styles.shareIcon} />
</View>
{!interested ? (
<View
style={{ backgroundColor: '#292929', width: 44, height: 44, borderRadius: 30 }}>
<Star style={styles.starIcon} onPress={handleInterest} />
</View>
) : (
<View
style={{ backgroundColor: '#007AFF', width: 44, height: 44, borderRadius: 30 }}>
<Star style={styles.starIcon} onPress={handleInterest} />
</View>
)}
<Image style={styles.roundAvatar} source={{ url: artist.media }} />
</View>
<View style={styles.iconWrapper}>
<Text style={styles.shareIconText}>Share</Text>
<Text style={styles.interestedIconText}>I'm Interested</Text>
<Text style={styles.artistIconText}>Add Artist</Text>
</View>
<View style={styles.ticketWrapper}>
<Text style={styles.ticketPrice}>TICKET PRICES</Text>
<QuestionMarkIcon style={styles.ticketPrice} />
</View>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<Text style={styles.ticketLocation}>In-location</Text>
<Text style={styles.ticketLocation}>$ {ticket_base_price}</Text>
</View>
<View
style={{
borderTopColor: '#DADADA',
borderTopWidth: 0.5,
borderStyle: 'solid',
height: 20,
marginTop: 32,
marginBottom: 28.5,
}}
/>
<View style={styles.footerWrapper}>
<Text style={styles.headerTitle}>TICKET GUIDE</Text>
<Text style={styles.ticketDescription}>
This ticket is for a livestream concert. Sign In at www.colortv.me on your browser
to watch on our TV or laptop. On the go? Watch from the COLOR TV mobile app.
</Text>
</View>
</View>
<Button
rightIcon={false}
text="Buy Ticket"
style={styles.button}
onPress={() => buyTicket(concertObj)}
/>
</ScrollView>
</View>
</>
);
}
ConcertPageScreen.propTypes = {
navigation: PropTypes.object,
route: PropTypes.object,
};
and here is the Context Provider:
import React, { createContext, useState } from 'react';
import { concertInterestApi } from 'utils/apiRoutes';
import useFetcher from 'hooks/useFetcher';
import parseError from '../utils/parseError';
export const ConcertContext = createContext(null);
const ConcertContextProvider = ({ children }) => {
const { isLoading, error, fetcher } = useFetcher();
const [interested, setInterested] = useState([]);
const concertInterest = async concertObj => {
try {
await fetcher({
url: concertInterestApi(concertObj.id),
method: concertObj.is_interested ? 'DELETE' : 'POST',
});
} catch ({ response }) {
throw parseError(response);
}
};
return (
<ConcertContext.Provider value={{ interested, setInterested, concertInterest }}>
{children}
</ConcertContext.Provider>
);
};
export default ConcertContextProvider;
Perhaps I'm not wiring this up correctly at all and any suggestions would be welcome to manage state.
You are confused about the shape of your own context. In your Provider, interested is an array (I'm not sure of what) and setInterested is a function to replace that array. With that in mind, hopefully you can see the problem with this:
const { interested, setInterested, concertInterest } = useContext(ConcertContext);
const handleInterest = () => {
setInterested(prevState => !prevState);
concertInterest(concertObj);
};
You are treating interested as a boolean when it is an array. I think it's all of the concerts that this user is interested it? Or perhaps an array of all userIds who are interested in this concert? Or perhaps useState([]) is a mistake and it was always meant to be a boolean?
Either way, I would recommend moving the shared logic from the ConcertCard and ConcertPageScreen into a custom hook that consumes your context and return an onClickInterested handler function. Your hook can take the concert id and/or user id as an argument.

API image not being displayed in list

I know there are several questions with this issue but mine is different.
I trying to display an image in my Flatlist card that is coming from an API. However it is not showing up.
BUT...when I display this image in another part of my code (in an Autocomplete list) using the same code basically, it works. Also, when I try an url from an image on the Web, it displays inside the flatlist correctly
Here's my Flatlist code:
<FlatList
data={this.state.myGamesArray}
renderItem={({ item }) => (
<Card>
<CardItem>
<View>
<Image
style={styles.gameImage}
source={{uri: item.background_image}}
/>
</View>
</CardItem>
<CardItem>
<View>
<Text style={styles.usergameText}>
{item}
</Text>
</View>
</CardItem>
</Card>
)}
keyExtractor={(item,index) => index.toString()}
/>
Here is my Autocomplete code in which I use the same image bracket-thingy
<View style={styles.iconContainer} >
<TouchableOpacity onPress={() => this.setState({ query: item.name})}
style={styles.autocompleteList} >
<View>
<Image
style={styles.gameImage}
source={{uri: item.background_image}}
/>
</View>
<Text style={styles.gameText}>
{item.name}
</Text>
</TouchableOpacity>
</View>
I ran console.item(item.background_image) both inside the Flatlist(first snippet) and the Autocomplete list (Second snippet). The first shows 'undefined' and the second it shows all the URIs
App.js full code:
/*This is an example of AutoComplete Input/ AutoSuggestion Input*/
import React, { Component } from 'react';
//import react in our code.
import { StyleSheet, Text, TouchableOpacity, View, Image, FlatList, Alert, TouchableWithoutFeedback, Keyboard } from 'react-native';
//import all the components we are going to use.
import Autocomplete from 'react-native-autocomplete-input';
import { Button, List, Container, ListItem, Card, CardItem, Header, Item } from 'native-base';
import { Entypo } from '#expo/vector-icons'
//import Autocomplete component
//const API = 'https://api.rawg.io/api/games?page=1';
//Demo base API to get the data for the Autocomplete suggestion
class App extends Component {
constructor(props) {
super(props);
//Initialization of state
//films will contain the array of suggestion
//query will have the input from the autocomplete input
this.state = {
myGamesArray: [],
games: [],
query: ' ',
};
}
componentDidMount() {
//First method to be called after components mount
//fetch the data from the server for the suggestion
fetch('https://api.rawg.io/api/games?page=1&platforms=18', {
"method": "GET",
"headers": {
"x-rapidapi-host": "rawg-video-games-database.p.rapidapi.com",
"x-rapidapi-key": "495a18eab9msh50938d62f12fc40p1a3b83jsnac8ffeb4469f"
}
})
.then(res => res.json())
.then(json => {
const { results: games } = json;
this.setState({ games });
//setting the data in the games state
});
}
findGame(query) {
let i;
//method called everytime when we change the value of the input
if (query === '') {
//if the query is null then return blank
return [];
}
const { games } = this.state;
//making a case insensitive regular expression to get similar value from the film json
const regex = new RegExp(`${query.trim()}`, 'i');
//return the filtered game array according the query from the input
return games.filter(game => game.name.search(regex) >= 0);
}
AddItemsToArray = () => {
var i
//verifica se input esta vazio
if (this.state.query === '') {
return Alert.alert('Voce não selecionou um jogo')
}
//VERIFY IF GAME IS IN THE ARRAY
for (i = 0; i < this.state.games.length - 1; i++) {
if (this.state.query !== this.state.games[i].name) {
if (i === this.state.games.length - 2) {
return Alert.alert('Este jogo nao existe')
}
else {
continue
}
} else {
break
}
}
//verifica repetido
if (this.state.myGamesArray.includes(this.state.query)) {
return Alert.alert('Este jogo já foi adicionado')
}
else {
//Adding Items To Array.
this.setState(prevState => {
const { myGamesArray, query } = prevState;
return {
myGamesArray: [...myGamesArray, query.toString()],
};
},
// Use setState callback to alert with the updated state
);
}
}
render() {
const { query } = this.state;
const games = this.findGame(query);
const comp = (a, b) => a.toLowerCase().trim() === b.toLowerCase().trim();
return (
<TouchableWithoutFeedback
onPress={() => {
Keyboard.dismiss()
}}
>
<View style={styles.container}>
<View style={styles.listContainer}
>
<FlatList
data={this.state.myGamesArray}
renderItem={({ item }) => (
console.log(item.background_image),
<Card style={{flexDirection:'row',paddingEnd:100}}>
<CardItem cardBody>
<View>
<Image
style={styles.listImage}
source={{uri: item.background_image}}
/>
</View>
</CardItem>
<CardItem>
<View>
<Text style={styles.usergameText}>
{item}
</Text>
<Text style={styles.usergameText}>
Playstation 4
</Text>
</View>
</CardItem>
</Card>
)}
keyExtractor={(item,index) => index.toString()}
/>
</View>
<View>
<Header span
style={styles.header}>
<Autocomplete
inputContainerStyle={{borderColor:'transparent'}}
style={styles.autocompleteInput}
autoCapitalize="none"
autoCorrect={false}
//data to show in suggestion
data={games.length === 1 && comp(query, games[0].name) ? [] : games}
//default value if you want to set something in input
defaultValue={query}
/*onchange of the text changing the state of the query which will trigger
the findGame method to show the suggestions*/
onChangeText={text => this.setState({ query: text })}
placeholder=" Adicione os jogos que você tem"
//This below is the 'list' of autocomplete options
renderItem={({ item }) => (
//you can change the view you want to show in suggestion from here
//I GET ERROR WHEN TRYING TO ERASE (PS4) IN TEXT BOX ***NEED TO CHECK THIS
<View style={styles.iconContainer} >
<TouchableOpacity onPress={() => this.setState({ query: item.name})}
style={styles.autocompleteList} >
<View>
<Image
style={styles.gameImage}
source={{uri: item.background_image}}
/>
</View>
<Text style={styles.gameText}>
`${item.name}`
</Text>
</TouchableOpacity>
</View>
)}
/>
</Header>
</View>
<TouchableOpacity
style={styles.addButton}
onPress={() => this.AddItemsToArray()}
>
<Entypo name="plus" size={50} color="#fff" />
</TouchableOpacity>
</View>
</TouchableWithoutFeedback>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#fff',
flex: 1,
},
autocompleteInput: {
borderWidth: 1,
backgroundColor: "#fff",
borderColor: '#7843FF',
height: 50,
marginTop: 70,
borderRadius:10,
},
autocompleteList: {
flex:1,
flexDirection: 'row',
borderWidth:0.5,
borderColor: '#7843FF',
paddingVertical: 5,
paddingRight: 60,
},
listContainer: {
flex: 1,
position: 'absolute',
left: 10,
right: 10,
top:150,
flexDirection:'column',
justifyContent: 'center',
borderColor: '#7843FF',
},
gameText: {
fontSize: 15,
marginLeft: 10,
marginRight:30,
marginVertical:10,
color: '#000',
textAlign: 'left',
justifyContent: 'center'
},
usergameText: {
fontSize:15,
textAlign: 'left',
alignSelf:'stretch',
color: '#000',
},
gameImage: {
flex: 3,
height: 60,
width: 60,
marginLeft:10,
borderRadius: 100,
},
listImage: {
flex: 3,
height: 110,
width: 90,
marginLeft:0,
},
addButton: {
height:50,
width: 50,
position: 'absolute',
left: 371,
top: 71,
backgroundColor: '#7843FF',
borderTopRightRadius: 10,
borderBottomRightRadius:10,
},
usergameImage: {
height: 100,
width: 100,
borderRadius: 100,
},
header: {
backgroundColor:'#67E6DC'
}
});
export default App;

React Navigation - pop() go's back to root rather than previous page

I am trying to add a back buttons into a bespoke sidemenu component.
Here is my navigation.js setup:
import { createAppContainer, createStackNavigator, createDrawerNavigator } from 'react-navigation';
import App from '../components/App';
import HomeView from '../components/HomeView';
import CheckIn from '../components/CheckIn';
import LoginView from '../components/LoginView';
import LrmDocs from '../components/LrmDocs';
import Rota from '../components/Rota';
import SideMenu from '../components/util/SideMenu';
const InitStack = createStackNavigator({
AppContainer: App,
LoginViewContainer:LoginView,
});
const RootStack = createDrawerNavigator({
Auth:InitStack,
HomeViewContainer: HomeView,
RotaContainer: Rota,
CheckInContainer:CheckIn,
LrmDocsContainer: LrmDocs,
},
{
drawerPosition: 'right',
contentComponent: SideMenu,
cardStyle: { backgroundColor: '#FFFFFF'},
drawerLockMode: 'locked-closed'
}
);
let Navigation = createAppContainer(RootStack);
export default Navigation;
I have the following setup in my bespoke sidemenu -
mport React, {Component} from 'react';
import CopyrightSpiel from './CopyrightSpiel';
import {ScrollView, Text, View, StyleSheet, Image, Button, TouchableOpacity} from 'react-native';
import { withNavigation } from 'react-navigation';
import { connect } from "react-redux";
import { authLogout, clearUser } from "../../store/actions/index";
class SideMenu extends Component {
constructor(props) {
super(props);
this.state = { loggedIn:false};
}
logOutHandler = () => {
this.props.onTryLogout();
this.props.clearUser();
this.props.navigation.navigate('AppContainer');
};
render() {
const isLoggedIn = () => {
if(this.state.loggedIn){
return true;
}
else {return false; }
};
let cp = this.props.activeItemKey;
let getCurrentCoordinates = (pg) => {
if(cp === pg){
return true;
}
};
return (
<View style={styles.container}>
<ScrollView>
<View style={styles.header}>
<View style={styles.closeBtnWrap}>
<TouchableOpacity
onPress={() => this.props.navigation.toggleDrawer() }
>
<Image
style={styles.closeBtnImg}
source={require('../../images/icons/ico-close.png')}
/>
</TouchableOpacity>
</View>
<View style={styles.logoBlock}>
<Image style={styles.homeBlockImg} source={require('../../images/loginLogo.png')} />
</View>
</View>
<View style={styles.navSection}>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('HomeViewContainer')}
>
<View style={[styles.navSectionStyle, getCurrentCoordinates('HomeViewContainer') && styles.navItemSel]}>
<Text style={styles.navItemStyle}>
HOME
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('RotaContainer')}
>
<View style={[styles.navSectionStyle, getCurrentCoordinates('RotaContainer') && styles.navItemSel]}>
<Text style={styles.navItemStyle} >
MY ROTA
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('LrmDocsContainer')}
>
<View style={[styles.navSectionStyle, getCurrentCoordinates('LrmDocsContainer') && styles.navItemSel]}>
<Text style={styles.navItemStyle} >
LRM DOCS
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity onPress={this.logOutHandler}>
<View style={styles.navSectionStyle}>
<Text style={styles.navItemStyle} >
SIGN OUT
</Text>
</View>
</TouchableOpacity>
</View>
<View style={styles.navSection}>
<Text style={styles.navSectionTitle}>Current Shift Options:</Text>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('CheckInContainer')}
>
<View style={[styles.navSectionStyle, isLoggedIn() && styles.checkedInHide, getCurrentCoordinates('CheckInContainer') && styles.navItemSel]}>
<Text style={styles.navItemStyle} >
CHECK IN
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('CheckInContainer')}
>
<View style={[styles.navSectionStyle, !(isLoggedIn()) && styles.checkedOutHide, getCurrentCoordinates('CheckInContainer') && styles.navItemSel]}>
<Text style={styles.navItemStyle} >
CHECK OUT
</Text>
</View>
</TouchableOpacity>
</View>
</ScrollView>
<View style={styles.footerContainer}>
<CopyrightSpiel color="LightGrey"/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
header:{
flexDirection:'row',
justifyContent:'center',
},
closeBtnWrap:{
position:'absolute',
left:15,
top:0,
opacity:0.8,
},
closeBtnImg:{
width:22,
height:22
},
container:{
backgroundColor:'#1C2344',
alignSelf:'stretch',
flex:1,
paddingTop:50,
borderLeftColor:'#F7931E',
borderLeftWidth:1
},
logoBlock:{
alignSelf:'center',
},
homeBlockImg:{
marginTop:10,
width:60,
height:105,
},
navSectionStyle:{
alignItems:'stretch',
textAlign: 'center',
backgroundColor:'rgba(255,255,255,0.1)',
paddingTop:8,
paddingBottom:8,
marginTop:15,
},
navItemSel:{
backgroundColor:'rgba(255,255,255,0.9)',
},
navItemSelText:{
color:'#1C2344',
},
navItemStyle:{
color:'#F7931E',
fontSize:24,
alignSelf:'center'
},
navSection:{
marginTop:30
},
navSectionTitle:{
color:'rgba(255,255,255,0.5)',
marginLeft:15,
},
footerContainer:{
paddingBottom:15,
},
checkedInHide:{
display:'none',
},
checkedOutHide:{
display:'none',
},
});
const mapDispatchToProps = dispatch => {
return {
onTryLogout: () => dispatch(authLogout()),
clearUser: () => dispatch(clearUser())
};
};
export default connect(null, mapDispatchToProps)(withNavigation(SideMenu));
which works fine -
I then have the following in my sub view header:
import React from 'react';
import {View, Image, Text, StyleSheet, TouchableOpacity, Button} from 'react-native';
import PropTypes from 'prop-types';
import { withNavigation } from 'react-navigation';
import { StackActions } from 'react-navigation';
class FixedHeader extends React.Component {
render() {
const popAction = StackActions.pop({
n: 0,
});
return (
<View style={FixedHeaderStyles.sectionHeader}>
<View style={FixedHeaderStyles.sectionHeaderTopLine}>
<View style={[FixedHeaderStyles.sectionHeaderBack, !(this.props.backButton) && FixedHeaderStyles.sectionHeaderHide ]}>
<TouchableOpacity
onPress={() => this.props.navigation.dispatch(popAction)}
>
<Text style={FixedHeaderStyles.sectionHeaderText}>< Back</Text>
</TouchableOpacity>
</View>
<View style={FixedHeaderStyles.logoBlock}>
<Image style={FixedHeaderStyles.homeBlockImg} source={require('../../images/logosmall.png')} />
</View>
<View style={FixedHeaderStyles.homeBlockBurger} >
<TouchableOpacity onPress={() => this.props.navigation.toggleDrawer() }>
<Image
style={FixedHeaderStyles.homeBurgerImg}
source={require('../../images/icons/ico-burger.png')}
/>
</ TouchableOpacity>
</View>
</View>
</View>
);
}
}
FixedHeader.propTypes = {
backButton: PropTypes.bool.isRequired,
navigate: PropTypes.object,
};
const FixedHeaderStyles = StyleSheet.create({
sectionHeadeLogo:{
width:45,
height:58,
alignSelf:'center'
},
sectionHeader:{
backgroundColor:'#1C2344',
flex:1.8,
alignSelf:'stretch',
borderBottomColor:'#f79431',
borderBottomWidth:1,
},
sectionHeaderTopLine:{
height:120,
paddingTop:45,
borderBottomColor:'#f79431',
borderBottomWidth:1,
flexDirection:'row',
justifyContent:'center',
alignItems:'center'
},
homeBlockBurger:{
position:'absolute',
right:0,
marginRight:15,
top:56
},
logoBlock:{
alignSelf:'flex-start',
},
homeBlockImg:{
width:45,
height:58,
alignSelf:'center',
},
homeBurgerImg:{
width:40,
height:40,
},
sectionHeaderHide:{
display:'none',
},
sectionHeaderBack:{
position:'absolute',
left:15,
top:70,
},
sectionHeaderText:{
color:'#fff',
},
});
export default withNavigation(FixedHeader);
The page navigates fine to the subview - but when pressing back the page jumps to the root (login page) rather than the previous page. For example - I navigate to rota from home, click back and jump back to login.. can anyone shine any light on this and point me in the correct direction - I've read the documentation but cant figure out whats going astray...
My Dependencies are as follows:
Here's how to do it in #react-navigation 6.x -we're in 2022 xD-
import { StackActions } from '#react-navigation/native';
const popAction = StackActions.pop(1);
navigation.dispatch(popAction);
documentation demo
https://reactnavigation.org/docs/stack-actions/#pop
snack demo https://snack.expo.dev/JEqNk9eBx
You shouldn't define your popAction with index to 0 in your FixedHeader class.
const popAction = StackActions.pop({
n: 0,
});
instead try
const popAction = StackActions.pop();
The pop action takes you back to a previous screen in the stack.
The n param allows you to specify how many screens to pop back
by.
Please refer to these docs: https://reactnavigation.org/docs/en/stack-actions.html#pop
Besides that, you would better define your const popAction = ... outside render() method.
import React from 'react';
import {View, Image, Text, StyleSheet, TouchableOpacity, Button} from 'react-native';
import PropTypes from 'prop-types';
import { withNavigation } from 'react-navigation';
import { StackActions } from 'react-navigation';
const popAction = StackActions.pop();
class FixedHeader extends React.Component {
render() {
return (
<View style={FixedHeaderStyles.sectionHeader}>
<View style={FixedHeaderStyles.sectionHeaderTopLine}>
<View style={[FixedHeaderStyles.sectionHeaderBack, !(this.props.backButton) && FixedHeaderStyles.sectionHeaderHide ]}>
<TouchableOpacity
onPress={() => this.props.navigation.dispatch(popAction)}
>
<Text style={FixedHeaderStyles.sectionHeaderText}>< Back</Text>
</TouchableOpacity>
</View>
<View style={FixedHeaderStyles.logoBlock}>
<Image style={FixedHeaderStyles.homeBlockImg} source={require('../../images/logosmall.png')} />
</View>
<View style={FixedHeaderStyles.homeBlockBurger} >
<TouchableOpacity onPress={() => this.props.navigation.toggleDrawer() }>
<Image
style={FixedHeaderStyles.homeBurgerImg}
source={require('../../images/icons/ico-burger.png')}
/>
</ TouchableOpacity>
</View>
</View>
</View>
);
}
}
FixedHeader.propTypes = {
backButton: PropTypes.bool.isRequired,
navigate: PropTypes.object,
};
const FixedHeaderStyles = StyleSheet.create({
sectionHeadeLogo:{
width:45,
height:58,
alignSelf:'center'
},
sectionHeader:{
backgroundColor:'#1C2344',
flex:1.8,
alignSelf:'stretch',
borderBottomColor:'#f79431',
borderBottomWidth:1,
},
sectionHeaderTopLine:{
height:120,
paddingTop:45,
borderBottomColor:'#f79431',
borderBottomWidth:1,
flexDirection:'row',
justifyContent:'center',
alignItems:'center'
},
homeBlockBurger:{
position:'absolute',
right:0,
marginRight:15,
top:56
},
logoBlock:{
alignSelf:'flex-start',
},
homeBlockImg:{
width:45,
height:58,
alignSelf:'center',
},
homeBurgerImg:{
width:40,
height:40,
},
sectionHeaderHide:{
display:'none',
},
sectionHeaderBack:{
position:'absolute',
left:15,
top:70,
},
sectionHeaderText:{
color:'#fff',
},
});
export default withNavigation(FixedHeader);
Cheers
Since you're using the withNavigation HOC, you'll want to use the navigation prop in your FixedHeader component instead of calling StackActions.pop. you can just call this.props.navigation.pop().
https://reactnavigation.org/docs/en/navigation-prop.html#navigator-dependent-functions
After a fair bit of head scratching and searching - I found an answer to my issue - My problem is that i'm using a drawer navigation rather than stack navigation - pop is not available to a drawer as there isnt an avilable stack -
ref - https://reactnavigation.org/docs/en/navigation-prop.html
answer from - https://github.com/react-navigation/react-navigation/issues/4793
The pop action takes you back to a previous screen in the stack. It takes one optional argument (count), which allows you to specify how many screens to pop back by.
import { StackActions } from '#react-navigation/native';
const popAction = StackActions.pop(1);
navigation.dispatch(popAction);

How can I update text of dynamic component on react native?

I´m new to react and react-native, I want to show a component that is composed of:
so when the user checks the corresponding checkbox I transform the mentioned component adding another view getting something like this:
The problem comes when the user clicks on the Text element because I show a modal to pick the desired hour and minutes and when this modal closes I'm pretending to update the state and this updates the Text element with the selected time but it´s not working, any advice?
this is my code:
import React from 'react';
import {View,Text, TextInput, StyleSheet,TouchableOpacity} from 'react-native';
import { CheckBox } from 'react-native-elements';
import DateTimePicker from 'react-native-modal-datetime-picker';
let frmHoras;
export default class CustomChkHorario extends React.Component{
constructor(props){
super(props);
this.state={
chk:false,
fontColor:'black',
title:'Closed',
isDateTimePickerVisible: false,
sTimeMonday:'00:00',
eTimeMonday:'00:00'
}
}
_showDateTimePicker = () => {
this.setState({ isDateTimePickerVisible: true });
}
_hideDateTimePicker = () => this.setState({ isDateTimePickerVisible: false });
_handleDatePicked = (date) => {
console.log('A date has been picked: ', date);
this.setState({sTimeMonday:date});
this._hideDateTimePicker();
};
pressedChk = () => {
//console.log('checked:',this.state.checked);
this.setState({chk:!this.state.chk}, () =>{
if(this.state.chk==false){
this.setState({fontColor:'black', title:'Closed'});
frmHoras=null;
}else{
this.setState({fontColor:'green', title:'Open'});
frmHoras=
<View style={{flexDirection:'row',alignItems:'center'}}>
<Text>from:</Text>
<TouchableOpacity onPress={this._showDateTimePicker}>
<Text style={styles.input}>{this.state.sTimeMonday}</Text>
</TouchableOpacity>
<Text>to:</Text>
<TouchableOpacity onPress={this._showDateTimePicker}>
<Text style={styles.input}></Text>
</TouchableOpacity>
</View>;
}
});
}
render(){
return(
<View style={{alignItems:'center'}}>
<View style={{flexDirection:'row',justifyContent:'center',alignItems:'center'}}>
<Text>{this.props.dia}</Text>
<CheckBox title={this.state.title}
checked={this.state.chk}
onPress={this.pressedChk}
checkedColor='green'
textStyle={{color:this.state.fontColor}}></CheckBox>
</View>
<View>
{frmHoras}
</View>
<DateTimePicker
isVisible={this.state.isDateTimePickerVisible}
onConfirm={this._handleDatePicked}
onCancel={this._hideDateTimePicker}
mode={'time'}
/>
</View>
);
}
}
const styles = StyleSheet.create({
input: {
marginHorizontal: 10,
height:25,
width:60,
borderBottomColor:'black',
borderStyle:'solid',
borderBottomWidth:1,
margin:10
}
});
_handleDatePicked = date => {
const time = new Date(date);
const timePicked = `${time.getHours()}:${time.getMinutes()}`;
this.setState({ sTimeMonday: timePicked });
this._hideDateTimePicker();
};
pressedChk = () => {
this.setState({ chk: !this.state.chk }, () => {
if (this.state.chk) {
this.setState({ fontColor: 'green', title: 'Open' });
} else {
this.setState({ fontColor: 'black', title: 'Closed' });
}
});
};
render() {
return (
<View style={{ alignItems: 'center' }}>
<View
style={{
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
}}>
<Text>{this.props.dia}</Text>
<CheckBox
title={this.state.title}
checked={this.state.chk}
onPress={this.pressedChk}
checkedColor="green"
textStyle={{ color: this.state.fontColor }}
/>
</View>
{this.state.chk && (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Text>from:</Text>
<TouchableOpacity onPress={this._showDateTimePicker}>
<Text style={styles.input}>{this.state.sTimeMonday}</Text>
</TouchableOpacity>
<Text>to:</Text>
<TouchableOpacity onPress={this._showDateTimePicker}>
<Text style={styles.input} />
</TouchableOpacity>
</View>
)}
<DateTimePicker
isVisible={this.state.isDateTimePickerVisible}
onConfirm={this._handleDatePicked}
onCancel={this._hideDateTimePicker}
mode={'time'}
/>
</View>
);
}
}
You should render frmHoras in a function, but you are currently doing that in the setState callback, which is a bit misleading in your case, as setState callback is called when setState is completed and the component is re-rendered. Look at the document which clearly mentioned about this behaviour
The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered.
So, changes will as follows
Create a new function which will render frmHoras, let it be renderFrmHoras
renderFrmHoras = () => {
const { chk } = this.state;
if(!chk) {
return null;
}
return (
<View style={{flexDirection:'row',alignItems:'center'}}>
<Text>from:</Text>
<TouchableOpacity onPress={this._showDateTimePicker}>
<Text style={styles.input}>{this.state.sTimeMonday}</Text>
</TouchableOpacity>
<Text>to:</Text>
<TouchableOpacity onPress={this._showDateTimePicker}>
<Text style={styles.input}></Text>
</TouchableOpacity>
</View>;
);
}
Next, update the pressedChk to set fontColor, title and chk values
pressedChk = () => {
this.setState((prevState) => {
return ({
chk: !prevState.chk,
fontColor: !prevState.chk ? 'green' : 'black',
title: !prevState.chk ? 'Open' : 'Closed'
});
})
}
Next, in the render method, just call the renderFrmHoras function
render() {
return (
...
</View>
<View>
{this.renderFrmHoras()}
</View>
<DateTimePicker
...
/>
...
);
}
Hope this will help!

unexpected token when trying to declare a variable inside flatlist in react native

i'm trying to declare an variable inside the FlatList component in React Native
But i get unexpected token, when i do declare it.
const FixturesScreen = ({ navigation }) => (
<ScrollView style={styles.container}>
<FlatList
data={clubData.data.clubs}
renderItem={({ item }) => (
let fixture = item.name //unexpected token
<View>
<View>
<Text style={styles.itemTitle}>{item.name}</Text>
<ScrollView horizontal>
<Text style={styles.listItem}>{fixture}</Text>
</ScrollView>
</View>
</View>
)
}
/>
</ScrollView>
)
here is my full FixturesScreen cdoe
import React from 'react'
import { View, Text, FlatList, ScrollView, StyleSheet } from 'react-native'
import Ionicons from 'react-native-vector-icons/Ionicons'
import clubData from '../../clubData'
const styles = StyleSheet.create({
container: {
backgroundColor: '#4BABF4',
},
itemTitle: {
color: 'black',
fontSize: 20,
marginTop: 20,
marginBottom: 10,
marginLeft: 15,
},
listItem: {
color: '#FFFFFF',
fontSize: 18,
textAlign: 'center',
marginRight: 15,
marginLeft: 15,
backgroundColor: '#77BEF5',
width: 120,
paddingVertical: 10,
},
})
const CURRENTTGAMEWEEK = 30
const i = CURRENTTGAMEWEEK
const nxt1 = i + 1
const nxt2 = nxt1 + 2
const nxt3 = nxt2 + 1
const nxt4 = nxt3 + 1
const nxt5 = nxt4 + 1
// let fixture
// const team = clubData.data.clubs[0].name
// const hTeam = clubData.data.clubs[0].fixtures[0].homeTeam
// const hTeamShort = clubData.data.clubs[0].fixtures[0].homeTeamShort
// const aTeamShort = clubData.data.clubs[0].fixtures[0].awayTeamShort
//
// if (team === hTeam) // working
// fixture = aTeamShort
// else
// fixture = hTeamShort
console.log(`Now is playing ${fixture}`)
const FixturesScreen = ({ navigation }) => (
<ScrollView style={styles.container}>
<FlatList
data={clubData.data.clubs}
renderItem={({ item }) => (
let fixture = item.name
<View>
<View>
<Text style={styles.itemTitle}>{item.name}</Text>
<ScrollView horizontal>
<Text style={styles.listItem}>{fixture}</Text>
</ScrollView>
</View>
</View>
)
}
/>
</ScrollView>
)
FixturesScreen.navigationOptions = {
tabBarTestIDProps: {
testID: 'TEST_ID_HOME',
accessibilityLabel: 'TEST_ID_HOME_ACLBL',
},
tabBarLabel: 'Main',
tabBarIcon: ({ tintColor, focused }) => (
<Ionicons
name={focused ? 'ios-home' : 'ios-home-outline'}
size={26}
style={{ color: tintColor }}
/>
),
}
export default FixturesScreen
So basically what i'm trying to do is declare homeTeam, awayTeam and Fixture inside the flatlist, so i can do an if/else conditional rendering inside the flatlist. I can achieve that outside the flatlist component but it is not right, because i can not compare all objects at once.
While using arrow functions () => ('someValue') is a shortcut for () => { return 'someValue'}.
(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
// equivalent to: (param1, param2, …, paramN) => { return expression; }
// Parentheses are optional when there's only one parameter name:
(singleParam) => { statements }
singleParam => { statements }
// A function with no parameters should be written with a pair of parentheses.
() => { statements }
// Parenthesize the body of function to return an object literal expression:
params => ({foo: bar})
So if you want to run some code before returning a value you should do like below;
const FixturesScreen = ({ navigation }) => (
<ScrollView style={styles.container}>
<FlatList
data={clubData.data.clubs}
renderItem={({ item }) => { //change to curly braces
let fixture = item.name;
// do something here
return (
<View>
<View>
<Text style={styles.itemTitle}>{item.name}</Text>
<ScrollView horizontal>
<Text style={styles.listItem}>{fixture}</Text>
</ScrollView>
</View>
</View>
);
}}
/>
</ScrollView>
)

Resources