how to add keys in this project - reactjs

im using react o nexpo xde and when i run the project i get a warning because my list doesnt hae keys, i want to know where and how to assing them, this is my code
import React, { Component } from 'react';
import { StyleSheet, Text, View,AppRegistry,Image,ActivityIndicator, FlatList,Navigator,TouchableHighlight, } from 'react-native';
import { StackNavigator } from 'react-navigation';
class Lista extends Component {
static navigationOptions = {
title: 'Lista',
}
constructor(props) {
super(props);
this.state = {
data:[]
};
}
load = async ()=>{
try{
let resp = await fetch('https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=fd829ddc49214efb935920463668608d')
let json = await resp.json()
this.setState({data:json.articles})
} catch (err) { console.log(err) }
}
componentDidMount(){this.load()}
render() {
return (
<View style={{ flex: 1}}>
<View style={{ flex:1,backgroundColor:'gray'}}>
<FlatList
data={this.state.data}
renderItem={({item}) => (
<TouchableHighlight onPress={() => this.props.navigation.navigate('Details', {item})}>
<View style={{ height:100,margin:15,backgroundColor:'skyblue', padding: 10, flexDirection: 'row'}}>
{item.urlToImage !== null &&
<Image source={{uri:item.urlToImage}} style={{width: 90, height: 80 }}/>
}
<View style={{ flex: 1 }}>
<Text style={{ textAlign: 'center',fontWeight: 'bold', fontSize: 18, color: 'white', flex:1, margin:10}}>{item.title}</Text>
<Text style={{ textAlign: 'right',fontWeight: 'bold', fontSize: 11, color: 'white'}}>{item.publishedAt}</Text>
</View>
</View>
</TouchableHighlight>
)}
/>
</View>
</View>
);
}
}
class DetailsScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
const { item } = navigation.state;
return {
title: item ? item.date : 'Details Screen',
}
};
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Image source={{uri:this.props.navigation.state.params.item.urlToImage}} style={{width: 90, height: 80 }}/>
<Text>{this.props.navigation.state.params.item.title}</Text>
<Text>{this.props.navigation.state.params.item.publishedAt}</Text>
</View>
);
}
}
const RootStack = StackNavigator(
{
Lista: {
screen: Lista,
},
Details: {
screen: DetailsScreen,
},
},
{
initialRouteName: 'Lista',
}
);
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
export default class App extends React.Component {
render() {
return <RootStack />;
}
}
i know it has to be something like, key={i} bu i hae tried in some ways and it doesnt work, im just learning react by myself so im a little confused here
ty so much

In your case you need to set up key to each child of <FlatList /> component. By react native docs recomended to use keyExtractor method defined in your component.
keyExtractor = (item, index) => index
render() {
return (
<View style={{ flex: 1}}>
<View style={{ flex:1,backgroundColor:'gray'}}>
<FlatList
data={this.state.data}
keyExtractor={this.keyExtractor}
renderItem={({item}) => (
<TouchableHighlight onPress={() => this.props.navigation.navigate('Details', {item})}>
<View style={{ height:100,margin:15,backgroundColor:'skyblue', padding: 10, flexDirection: 'row'}}>
{item.urlToImage !== null &&
<Image source={{uri:item.urlToImage}} style={{width: 90, height: 80 }}/>
}
<View style={{ flex: 1 }}>
<Text style= {{ textAlign: 'center',fontWeight: 'bold', fontSize: 18, color: 'white', flex:1, margin:10}}>{item.title}</Text>
<Text style= {{ textAlign: 'right',fontWeight: 'bold', fontSize: 11, color: 'white'}}>{item.publishedAt}</Text>
</View>
</View>
</TouchableHighlight>
)}
/>
</View>
</View>
);
}
I set just index of element as key, but you can set as you wont, but make sure it is unique. But using indexes is bad practice, it is not safe.

Related

store.dispatch not updating props when called from outside of class

store.js
import AsyncStorage from "#react-native-community/async-storage";
import { createStore, applyMiddleware } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import { createLogger } from "redux-logger";
import thunk from "redux-thunk";
import reducers from "../reducers";
const logger = createLogger({
// ...options
});
const persistedReducer = persistReducer(persistConfig, reducers);
export default () => {
let store = createStore(persistedReducer, {}, applyMiddleware(thunk, logger));
let persistor = persistStore(store);
return { store, persistor };
};
PathexploredTab.js
import * as React from "react";
import {
View,
StyleSheet,
Text,
Dimensions,
TouchableOpacity,
Alert,
Platform,
Image
} from "react-native";
import { TabView, SceneMap, TabBar } from "react-native-tab-view";
import { Config } from "#common";
import { connect } from "react-redux";
import { compose } from "redux";
import { Dropdown } from "react-native-material-dropdown";
import { Field, reduxForm } from "redux-form";
import { ScrollView } from "react-native-gesture-handler";
import { Actions } from "react-native-router-flux";
import Icon from "react-native-vector-icons/Feather";
import {
searchImmipaths,
isKeepData,
backToInitialState,
continueFromPrevious,
fetchDynamicFacts,
pathExplorerTutorial
} from "../actions/path.actions";
import { checkConnection } from "../service/checkConnection";
import Loader from "./Loader";
import { colors, normalize, family, Images } from "#common";
import ResponsiveImage from 'react-native-responsive-image';
import { RFValue } from "react-native-responsive-fontsize";
import RNPicker from "rn-modal-picker";
import * as Animatable from 'react-native-animatable';
import UserinfoPopUp from "./../pages/UserinfoPopUp";
import { updateUserDetails, getUserDetails } from "../actions/auth.actions";
import AsyncStorage from "#react-native-community/async-storage";
import { copilot, walkthroughable, CopilotStep } from 'react-native-copilot';
import { StepNumberComponent } from "./../pages/newProfileScreen";
import persist from "./../config/store";
import Reactotron from 'reactotron-react-native'
const WalkthroughableImage = walkthroughable(Image);
const WalkthroughableText = walkthroughable(Text);
const WalkthroughableView = walkthroughable(View);
const WalkthroughableTouch = walkthroughable(TouchableOpacity);
const persistStore = persist();
const TooltipComponent = ({
isFirstStep,
isLastStep,
handleNext,
handlePrev,
handleStop,
labels,
currentStep,
}) => {
const handleDiscardTutorial = () => {
Alert.alert(
'',
'are you sure you don’t want a tutorial on how to use the app?',
[
{
text: 'yes',
onPress: () => {AsyncStorage.setItem('DontShowTutorial', JSON.stringify(true)), handleStop()}
},
{
text: 'Cancel',
onPress: () => console.log('Cancel Pressed'),
style: 'cancel'
},
],
{ cancelable: false }
);
}
return (
<View style={styles.tooltipContainer}>
<Text testID="stepDescription" style={styles.tooltipText}>{currentStep.text}</Text>
<View style={[styles.bottomBar]}>
{
!isFirstStep ?
<TouchableOpacity style={styles.toolTipButton} onPress={handlePrev}>
<Text style={styles.toolTipButtonText}>{'Previous'}</Text>
</TouchableOpacity>
: null
}
{
!isLastStep ?
<TouchableOpacity style={styles.toolTipButton} onPress={handleNext}>
<Text style={styles.toolTipButtonText}>{'Next'}</Text>
</TouchableOpacity> :
<TouchableOpacity style={styles.toolTipButton} onPress={handleStop}>
<Text style={styles.toolTipButtonText}>{'Finish'}</Text>
</TouchableOpacity>
}
<TouchableOpacity onPress={() => persistStore.store.dispatch(pathExplorerTutorial('bbv'))}>
<Text style={styles.toolTipButtonText}>Go</Text>
</TouchableOpacity>
<TouchableOpacity onPress={handleDiscardTutorial}>
<Text style={styles.toolTipButtonText}>Do not show tutorial</Text>
</TouchableOpacity>
</View>
</View>
);
}
const window = Dimensions.get("window");
var width = window.width;
var height = window.height;
var immigrationInterst = [];
var countryIntrest = [];
let countryIntrestNow = [];
var FirstRoute = () => <View style={[{ height: height }]} />;
let disableButton = false;
var lastSearchedCountries = [];
var SecondRoute = () => <View style={[{ height: height }]} />;
class PathexploredTab extends React.Component {
constructor(props) {
super(props);
this.imageMap = ['', Images.studyImage, Images.workImage, Images.residencyImage, Images.tourismImage];
this.state = {
isloading: false,
buttonone: false,
immibut1: false,
immibut2: false,
immibut3: false,
immibut4: false,
userInfoPopUpVisible: false,
countrybut1: false,
countrybut2: false,
countrybut3: false,
countrybut4: false,
filedsToShow: [],
countrybut5: false,
countrybut6: false,
selectedval: "Select the Country",
index: 0,
routes: [
{ key: "first", title: "Select Goals" },
{ key: "second", title: "Select Countries" }
],
checkconnection: false,
errorMessage: "",
isItemSelected: true,
currentlySelectedItemIndex: -1,
isItemChecked: true,
checkboxData: ""
};
}
componentDidUpdate = (nextProps) => {
Reactotron.log('=====', nextProps.userPressedGo)
}
componentWillReceiveProps = (nextProps) => {
}
componentWillMount = async() => {
const {
getUser: { userDetails },
authData: { token }
} = this.props;
this.props.dispatch(getUserDetails(userDetails.userid, token))
}
componentDidMount() {
setTimeout(() => {
this.props.start();
}, 1000);
immigrationInterst = [];
countryIntrest = [];
countryIntrestNow = [];
const {
getUser: { userDetails }
} = this.props;
}
callProceed = () => {
const {
isItemSelected,
currentlySelectedItemIndex,
isItemChecked
} = this.state;
if (isItemSelected) {
Alert.alert("Immigreat", "Please select any goal");
return;
}
const lastGoal = this.props.immigationInterest[0]
//This is a bit of a hacky fix but it works.
let allPrevCountriesFound = lastSearchedCountries.every(ai => countryIntrestNow.includes(ai))
&& (lastSearchedCountries.length == countryIntrestNow.length);
//We want to clear and search when:
//a) There is no exploration id
//b) Even if there is an exploration id but the lastGoal or last countries dont match
//Note: We make one exception for if exploration id exists but the lastSearchedCountries is just an empty list
let noExplorationId = (this.props.explorationId === "");
let countryGoalDontMatch = (lastGoal != currentlySelectedItemIndex || !allPrevCountriesFound);
let isProceedException = (lastSearchedCountries.length == 0 && !noExplorationId);
noExplorationId || (!noExplorationId && countryGoalDontMatch && !isProceedException)
?
this.clearAndSearch()
: Alert.alert(
"Immigreat",
"Previous exploration found. Would you like to continue?",
[
{
text: "START FROM BEGINNING",
onPress: () => {
this.props.dispatch(continueFromPrevious(0));
this.props.dispatch(backToInitialState());
this.props.dispatch(isKeepData(false));
this._searchData();
},
style: "cancel"
},
{
text: "CONTINUE",
onPress: () => {
lastSearchedCountries = [...countryIntrest];
this.props.dispatch(isKeepData(true));
this.props.didTapOnSearch();
}
},
{
text: "Cancel",
onPress: () => console.log("Canceled"),
style: "cancel"
}
],
{ cancelable: false }
);
}
_onPressBackButton = () => {
this.setState(
{
isItemSelected: true,
currentlySelectedItemIndex: -1,
isItemChecked: false
},
() => {
setTimeout(() => {
this.intialLoadValues();
//this.setState({ isItemSelected: true });
}, 0);
}
);
}
extraOptionsRefresh() {
const { currentlySelectedItemIndex, isItemChecked } = this.state;
this.setState({ isItemChecked: !isItemChecked })
switch (currentlySelectedItemIndex) {
case 1:
this.setState({checkBoxData:
"Do you want to explore options you may have after your studies?"});
break;
case 2:
this.setState({checkBoxData:
"Do you want to explore other goal options (such as studies) that can lead to work options?"});
break;
case 3:
this.setState({checkBoxData:
"Do you want to explore other goal options (such as studies or work) that could lead to permanent residency in the future?"});
break;
case -1:
this.setState({checkBoxData: ""});
break;
}
}
intialLoadValues() {
const { currentlySelectedItemIndex, isItemChecked } = this.state;
/*var checkBoxData = "";
switch (currentlySelectedItemIndex) {
case 1:
checkBoxData =
"Do you want to explore options you may have after your studies?";
break;
case 2:
checkBoxData = "Do you want to explore other goal options (such as studies) that can lead to work options?";
break;
case 3:
checkBoxData =
"Do you want to explore other goal options (such as studies or work) that could lead to permanent residency in the future?";
break;
case 4:
checkBoxData = "";
break;
}*/
FirstRoute = () => (
<ScrollView style={{ flex: 1, backgroundColor: "white" }}>
<Animatable.View animation="fadeIn" duration={1200} style={{ flex: 1 }}>
<View
style={{
backgroundColor: "#DBDDDF",
alignItems: "center",
justifyContent: "center",
flex: 1
}}
>
<View style={{ padding: 20 }}>
<Text
style={{
textAlign: "center",
fontFamily: "SourceSansPro-Semibold",
color: "#2C393F",
fontSize: RFValue(12)
}}
>
Select the Immigration goal that you would be interested in
exploring
</Text>
<Text
style={{
textAlign: "center",
color: "#008BC7",
fontFamily: "SourceSansPro-Regular",
fontSize: RFValue(10)
}}
>
You can select/deselect ONLY one goal at a time by clicking the buttons. This is so we can help you hone your search!
</Text>
</View>
</View>
{(this.state.isItemSelected && this.state.currentlySelectedItemIndex < 0) ? (
<View style={{ marginTop: 30 }}>
{/*<Animatable.View animation="fadeIn" duration={1200} style={{ flexGrow: 1, justifyContent: 'center', alignItems: 'center', flexDirection: 'row' }}>*/}
<View style={styles.custombuttonView}>
<View style={{ width: "50%" }}>
<CopilotStep text="Select Your goals" order={1} name="study">
<WalkthroughableTouch
testID={`${Config.immigerat[0].code + 'button'}`}
onPress={() =>
this._immigreateIntrest(Config.immigerat[0].code)
}
style={
this.state.immibut1
? styles.selectedbutton
: styles.buttonstyleview
}
>
<WalkthroughableText
style={
this.state.immibut1
? styles.selectedbuttontext
: styles.buttonstyletext
}
>
{Config.immigerat[0].value}
</WalkthroughableText>
</WalkthroughableTouch>
</CopilotStep>
</View>
<View style={{ width: "50%" }}>
<TouchableOpacity
testID={`${Config.immigerat[1].code + 'button'}`}
onPress={() =>
this._immigreateIntrest(Config.immigerat[1].code)
}
style={
this.state.immibut2
? styles.selectedbutton
: styles.buttonstyleview
}
>
<Text
style={
this.state.immibut2
? styles.selectedbuttontext
: styles.buttonstyletext
}
>
{Config.immigerat[1].value}
</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.custombuttonView}>
<View style={{ width: "50%" }}>
<TouchableOpacity
testID={`${Config.immigerat[2].code + 'button'}`}
onPress={() =>
this._immigreateIntrest(Config.immigerat[2].code)
}
style={
this.state.immibut3
? styles.selectedbutton
: styles.buttonstyleview
}
>
<Text
style={
this.state.immibut3
? styles.selectedbuttontext
: styles.buttonstyletext
}
>
{Config.immigerat[2].value}
</Text>
</TouchableOpacity>
</View>
<View style={{ width: "50%" }}>
<TouchableOpacity
testID={`${Config.immigerat[3].code + 'button'}`}
onPress={() =>
this._immigreateIntrest(Config.immigerat[3].code)
}
style={
this.state.immibut4
? styles.selectedbutton
: styles.buttonstyleview
}
>
<Text
style={
this.state.immibut4
? styles.selectedbuttontext
: styles.buttonstyletext
}
>
{Config.immigerat[3].value}
</Text>
</TouchableOpacity>
</View>
</View>
{/*</Animatable.View>*/}
</View>
) : (
<View style={{ flex: 0.75, marginTop: 30 }}>
{/*<Animatable.View animation="fadeIn" duration={1200}>*/}
<View style={{ flexDirection: 'row', alignContent: 'center' }}>
<TouchableOpacity testID='goBackButton'
onPress={this._onPressBackButton} style={{ padding: 10 }} >
<View style={{ width: 80 }}>
<Icon name="chevron-left" size={30} color="black" />
</View>
</TouchableOpacity>
{<TouchableOpacity
onPress={() => this._immigreateIntrest("null")}
style={[styles.selectedbutton, { width: "50%" }]}
>
<Text style={styles.selectedbuttontext}>
{Config.immigerat[this.state.currentlySelectedItemIndex - 1].value}
</Text>
</TouchableOpacity>}
</View>
{currentlySelectedItemIndex !== 4 && (
<View
style={{
borderWidth: 1,
borderRadius: 3,
borderColor: "rgba(110,110,110,0.4)",
flex: 1,
marginHorizontal: 10,
marginTop: 30,
flexDirection: "row"
}}
>
<TouchableOpacity
activeOpacity={0.8}
onPress={() => {
this.extraOptionsRefresh()
//setTimeout(() => {
//this.extraOptionsRefresh();
//this.intialLoadValues();
//this.setState({ isItemChecked: !isItemChecked });
//}, 100);
}
}
style={{
// flex: 0.35,
alignItems: "center",
justifyContent: "center",
marginLeft: 15
}}
hitSlop={{ top: 20, bottom: 20, left: 50, right: 50 }}
>
<View
style={[
styles.button,
{
backgroundColor: "white",
borderColor: "rgba(110,110,110,0.4)",
borderWidth: 1,
borderRadius: 2,
width: 20,
height: 20
}
]}
>
{this.state.isItemChecked ? (
<Icon name={"check"} color={colors.LIGHT_BLUE} size={15} />
) : null}
</View>
</TouchableOpacity>
<Text
style={{
flex: 1,
marginVertical: 10,
marginLeft: 10,
fontFamily: "SourceSansPro-Bold",
color: "#242424",
fontSize: RFValue(12)
}}
>
{this.state.checkBoxData}
</Text>
</View>
)}
{(!this.state.isItemSelected && this.state.currentlySelectedItemIndex > 0) &&
<Animatable.View animation="fadeIn" duration={1200} style={{ flexGrow: 1, justifyContent: 'center', alignItems: 'center', flexDirection: 'row' }}>
<ResponsiveImage source={this.imageMap[this.state.currentlySelectedItemIndex]} initWidth="270" initHeight="220" />
</Animatable.View>
}
</View>
)}
<View>
<TouchableOpacity
testID='searchButton'
onPress={() => !disableButton && this.showAlert1()}
style={{
width: 130,
justifyContent: "center",
backgroundColor: colors.LIGHT_BLUE,
borderRadius: 100,
padding: 10,
alignSelf: "center",
marginTop: height > 700 ? 60 : 40,
marginBottom: 20
}}
>
<Text
style={{
textAlign: "center",
color: "#fff",
fontFamily: "SourceSansPro-Regular"
}}
>
Search
</Text>
</TouchableOpacity>
</View>
</Animatable.View>
</ScrollView>
);
SecondRoute = () => (
<ScrollView style={{ flex: 1, backgroundColor: "white" }}>
<View style={{ backgroundColor: "white" }}>
<View
style={{
alignSelf: "center",
justifyContent: "center",
width: width
}}
>
<View style={{ margin: 20, alignSelf: "center" }}>
<Text
style={{
textAlign: "center",
fontFamily: "SourceSansPro-Semibold",
color: "#2C393F",
fontSize: 15
}}
>
Select all countries you are keen to explore
</Text>
<Text
style={{
textAlign: "center",
color: "rgba(44, 57, 63,0.6)",
marginTop: 20,
fontFamily: "SourceSansPro-Semibold"
}}
>
From:
</Text>
<View style={styles.loginView}>
<Field
name="selectcountry"
placeholder={this.state.selectedval}
component={this.renderDropdown}
data={Config.countries}
/>
</View>
<Text
style={{
textAlign: "center",
color: "rgba(44, 57, 63,0.6)",
marginTop: 20,
fontFamily: "SourceSansPro-Semibold"
}}
>
To:
</Text>
</View>
</View>
<View>
<View style={styles.custombuttonView}>
<View style={{ width: "33%" }}>
<TouchableOpacity
testID={`${Config.intrestcountry[0].value + 'interestButton'}`}
onPress={() =>
this._countryIntrest(Config.intrestcountry[0].code)
}
>
<Image
style={styles.countryIcon}
source={this.state.countrybut1
? Images.usa_selected
: Images.usa_unavailable}
/>
</TouchableOpacity>
</View>
<View style={{ width: "33%" }}>
<TouchableOpacity
testID={`${Config.intrestcountry[1].value + 'interestButton'}`}
>
<Image
style={styles.countryIcon}
source={this.state.countrybut2
? Images.canada_selected
: Images.canada_unavailable}
/>
</TouchableOpacity>
</View>
<View style={{ width: "33%" }}>
<TouchableOpacity
testID={`${Config.intrestcountry[2].value + 'interestButton'}`}
onPress={() =>
this._countryIntrest(Config.intrestcountry[2].code)
}
>
<Image
</TouchableOpacity>
</View>
</View>
<View style={styles.custombuttonView}>
<View style={{ width: "33%" }}>
<TouchableOpacity
testID={`${Config.intrestcountry[3].value + 'interestButton'}`}
onPress={() =>
this._countryIntrest(Config.intrestcountry[3].code)
}
>
<Image
/>
</TouchableOpacity>
</View>
<View style={{ width: "33%" }}>
<TouchableOpacity
testID={`${Config.intrestcountry[4].value + 'interestButton'}`}
onPress={() =>
this._countryIntrest(Config.intrestcountry[4].code)
}
>
<Image
</TouchableOpacity>
</View>
<View style={{ width: "33%" }}>
<TouchableOpacity
testID={`${Config.intrestcountry[5].value + 'interestButton'}`}
onPress={() =>
this._countryIntrest(Config.intrestcountry[5].code)
}
>
<Image
/>
</TouchableOpacity>
</View>
</View>
</View>
<View>
<TouchableOpacity
testID='countrySearchButton'
onPress={() => !disableButton && this.showAlert1()}
style={{
}}
>
<Text
>
Search
</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>
);
}
_countryIntrest(code) {
// alert(code);
var index = countryIntrest.indexOf(code);
var butstr = "countrybut" + code;
if (index == -1) {
countryIntrest.push(code);
countryIntrestNow.push(code);
this.setState({ [butstr]: true });
this.intialLoadValues();
} else {
countryIntrest.splice(index, 1);
countryIntrestNow.splice(index, 1);
this.setState({ [butstr]: false });
this.intialLoadValues();
}
}
_immigreateIntrest = async(code) => {
if(code === 4){
)
return;
}
const { isItemSelected } = this.state;
if (code === "null") {
);
} else {
await this.setState(
);
}
}
renderScene = ({ route }) => {
switch (route.key) {
case 'first':
case 'second':
default:
return null;
}
};
render() {
return (
<View style={{ flex: 1 }}>
{this.props.isLoading && (
<View
style={{
}}
>
<Loader />
</View>
)}
<TabView
removeClippedSubviews={Platform.OS === "android" ? true : false}
style={{
backgroundColor: this.state.index === 0 ? "#DBDDDF" : "white"
}}
navigationState={this.state}
removeClippedSubviews={Platform.OS === "android" ? true : false}
renderTabBar={props => (
<TabBar
}}
pass
getLabelText={({ route }) => route.title}
/>
)}
/>
<UserinfoPopUp
visible={this.state.userInfoPopUpVisible}
onClose={()=>this.setState({ userInfoPopUpVisible: false })}
userPopUpSubmit={this._userPopUpSubmit.bind(this)}
/>
</View>
);
}
}
mapStateToProps = state => ({
userPressedGo: state.pathReducer.getImmipathDetails.userPressedGo,
});
mapDispatchToProps = dispatch => ({
dispatch
});
export default compose(
connect(
mapStateToProps,
mapDispatchToProps
),
reduxForm({
form: "pathexplorertab"
// validate
})
)(copilot({
tooltipComponent: TooltipComponent,
stepNumberComponent: StepNumberComponent
})(PathexploredTab));
Pathexplorer.js contains Pathexplorer class in which there is an component called ToolTipComponent from which i am calling an action but the action isn't reflected in mapStateToProps. The reason i am forced to do this is that i am using a library called react-native-copilot in which i have a custom tooltip component from which i want to access the state
Problem in this event:
onPress={store.dispatch(action)}
should be:
onPress={()=>store.dispatch(action)}
By the way you can connect your functional component like class component:
export default connect(
mapStateToProps,
mapDispatchToProps
)
)(TooltipComponent)

how to update state according to id?

I have a touchableopacity on each card where I want to setstate of expand to true, but I want to do it according to the id, so that state of only one changes, any idea how to do it using map()?
My code:
import React, {useState, useEffect} from 'react';
import {
SafeAreaView,
Text,
Image,
ScrollView,
TouchableOpacity,
View,
} from 'react-native';
import axios from 'axios';
import {ROOT} from '../../../../ApiUrl';
import Icon from 'react-native-vector-icons/FontAwesome';
export default function VaccinationListScreen(props) {
const [expand, setExpand] = useState(false);
const [data, setData] = useState('');
let id = props.route.params.id;
const getData = () => {
let url = `some url`;
console.log('bbb');
axios
.get(url)
.then(function (res) {
console.log(res.data.content);
setData(res.data.content);
})
.catch(function (err) {
console.log(err);
});
};
useEffect(() => {
getData();
}, []);
return (
<SafeAreaView>
<ScrollView>
<TouchableOpacity style={{padding: 10}} onPress={()=>setExpand(true)}>
{data != undefined &&
data != null &&
data.map((item) => {
return (
<View
style={{
padding: 10,
backgroundColor: '#fff',
elevation: 3,
margin: '2%',
borderRadius: 5,
}}
key={item.id}>
<View style={{alignItems: 'flex-end'}}>
<Text style={{color: 'grey', fontSize: 12}}>
{item.display_date}
</Text>
</View>
<View style={{flexDirection: 'row'}}>
<View>
<Image
source={require('../../assets/atbirth.jpg')}
style={{height: 40, width: 50}}
resizeMode="contain"
/>
</View>
<View style={{flex: 1}}>
<View style={{flexDirection: 'row', flex: 1}}>
<Text
key={item.id}
style={{
fontFamily: 'Roboto',
fontSize: 18,
fontWeight: 'bold',
}}>
{item.name}
</Text>
</View>
<View style={{flexDirection: 'row', width: '30%'}}>
{item.vaccine_list.map((i) => {
return (
<View style={{flexDirection: 'row'}}>
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{fontFamily: 'Roboto', fontSize: 15}}>
{i.name},
</Text>
</View>
);
})}
</View>
</View>
</View>
<View style={{alignItems: 'flex-end', marginTop: '1%'}}>
<View style={{flexDirection: 'row'}}>
<Text
style={{
color: 'red',
fontSize: 14,
fontWeight: 'bold',
}}>
{item.child_vacc_status.text}
</Text>
<Icon
name="chevron-up"
color="red"
size={12}
style={{marginTop: '1%', marginLeft: '1%'}}
/>
</View>
</View>
</View>
);
})}
</TouchableOpacity>
</ScrollView>
</SafeAreaView>
);
}
Any suggestions would be great, do let mw know if anything else is required for better understanding
As i review your code <TouchableOpacity> wraps all of your cards at once not on each card set. If you implement your code that way if it's not impossible it will be difficult for you to reference each cards id and set the state of expand to true according to cards id.
My suggestion is to include <TouchableOpacity> to map() function nest so that it will be easy to reference each cards function.
I reproduce this specific problem and implement a solution in which I was able to set the state of expand to true according to each cards id.
You may click the sandbox link to see a demonstration.
https://codesandbox.io/s/accordingtoid-4px1w
Code in Sandbox:
import React, { useState, useEffect } from "react";
import {
SafeAreaView,
Text,
Image,
TouchableOpacity,
View
} from "react-native";
// import axios from 'axios';
// import {ROOT} from '../../../../ApiUrl';
// import Icon from "react-native-vector-icons/FontAwesome";
export default function VaccinationListScreen(props) {
const [expand, setExpand] = useState({});
const [data, setData] = useState([]);
// let id = props.route.params.id;
// const getData = () => {
// let url = `some url`;
// console.log('bbb');
// axios
// .get(url)
// .then(function (res) {
// console.log(res.data.content);
// setData(res.data.content);
// })
// .catch(function (err) {
// console.log(err);
// });
// };
// useEffect(() => {
// getData();
// }, []);
useEffect(() => {
// In order to simulate and reproduce the problem
// Assume that these are the data that you fetch from an API
const dataContent = [
{
id: 1,
name: "At Birth",
display_date: "02 May - 08 May 16",
vaccine_list: [
{ name: "BCG" },
{ name: "Hepatitis B" },
{ name: "OPV 0" }
],
child_vacc_status: { text: "Missed" }
},
{
id: 2,
name: "At 6 Weeks",
display_date: "02 May - 08 May 16",
vaccine_list: [
{ name: "IPV" },
{ name: "PCV" },
{ name: "Hepatitis b" },
{ name: "DTP" },
{ name: "HiB" },
{ name: "Rotavirus" }
],
child_vacc_status: { text: "Missed" }
}
];
setData(dataContent);
}, []);
function handleOnPress(id) {
setExpand((prev) => {
let toggleId;
if (prev[id]) {
toggleId = { [id]: false };
} else {
toggleId = { [id]: true };
}
return { ...toggleId };
});
}
useEffect(() => {
console.log(expand); // check console to see the value
}, [expand]);
return (
<SafeAreaView>
{data !== undefined &&
data !== null &&
data.map((item) => {
return (
<TouchableOpacity
key={item.id}
style={{
padding: 10
}}
onPress={() => handleOnPress(item.id)}
>
<View
style={{
padding: 10,
backgroundColor: expand[item.id] ? "lightgrey" : "#fff",
elevation: 3,
margin: "2%",
borderRadius: 5
}}
>
<View style={{ alignItems: "flex-end" }}>
<Text style={{ color: "grey", fontSize: 12 }}>
{item.display_date}
</Text>
</View>
<View style={{ flexDirection: "row" }}>
<View>
<Image
// source={require('../../assets/atbirth.jpg')}
style={{ height: 40, width: 50 }}
resizeMode="contain"
/>
</View>
<View style={{ flex: 1 }}>
<View style={{ flexDirection: "row", flex: 1 }}>
<Text
key={item.id}
style={{
fontFamily: "Roboto",
fontSize: 18,
fontWeight: "bold"
}}
>
{item.name}
</Text>
</View>
<View style={{ flexDirection: "row", width: "30%" }}>
{item.vaccine_list.map((item, i) => {
return (
<View key={i} style={{ flexDirection: "row" }}>
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{ fontFamily: "Roboto", fontSize: 15 }}
>
{item.name},
</Text>
</View>
);
})}
</View>
</View>
</View>
<View style={{ alignItems: "flex-end", marginTop: "1%" }}>
<View style={{ flexDirection: "row" }}>
<Text
style={{
color: "red",
fontSize: 14,
fontWeight: "bold"
}}
>
{item.child_vacc_status.text}
</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
})}
</SafeAreaView>
);
}
I haven't tested the code to work correctly, but you could try something similar. You could create a separate component for the items and set a status for each of them.
export default function VaccinationListScreen(props) {
const [expand, setExpand] = useState(false);
const [data, setData] = useState("");
const VaccinationListItem = (item) => {
const [expand, setExpand] = useState(false);
return (
<TouchableOpacity style={{ padding: 10 }} onPress={() => setExpand(true)}>
<View
style={{
padding: 10,
backgroundColor: "#fff",
elevation: 3,
margin: "2%",
borderRadius: 5,
}}
key={item.id}
>
<View style={{ alignItems: "flex-end" }}>
<Text style={{ color: "grey", fontSize: 12 }}>
{item.display_date}
</Text>
</View>
<View style={{ flexDirection: "row" }}>
<View>
<Image
source={require("../../assets/atbirth.jpg")}
style={{ height: 40, width: 50 }}
resizeMode="contain"
/>
</View>
<View style={{ flex: 1 }}>
<View style={{ flexDirection: "row", flex: 1 }}>
<Text
key={item.id}
style={{
fontFamily: "Roboto",
fontSize: 18,
fontWeight: "bold",
}}
>
{item.name}
</Text>
</View>
<View style={{ flexDirection: "row", width: "30%" }}>
{item.vaccine_list.map((i) => {
return (
<View style={{ flexDirection: "row" }}>
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{ fontFamily: "Roboto", fontSize: 15 }}
>
{i.name},
</Text>
</View>
);
})}
</View>
</View>
</View>
<View style={{ alignItems: "flex-end", marginTop: "1%" }}>
<View style={{ flexDirection: "row" }}>
<Text
style={{
color: "red",
fontSize: 14,
fontWeight: "bold",
}}
>
{item.child_vacc_status.text}
</Text>
<Icon
name="chevron-up"
color="red"
size={12}
style={{ marginTop: "1%", marginLeft: "1%" }}
/>
</View>
</View>
</View>
</TouchableOpacity>
);
};
return (
<SafeAreaView>
<ScrollView>
{data != undefined &&
data != null &&
data.map((item) => {
VaccinationListItem(item);
})}
</ScrollView>
</SafeAreaView>
);
}
Generally if you want to toggle any single element you should store its id in expand (instead of a boolean), and simply check when rendering the array if any specific element's id matches, i.e. element.id === expand. When any new element is touched, pop its id in there, if the id is already there, set to null to collapse.
export default function VaccinationListScreen(props) {
const [expandId, setExpandId] = useState(null); // <-- stores null or id, initially null
...
// Create curried handler to set/toggle expand id
const expandHandler = (id) => () =>
setExpandId((oldId) => (oldId === id ? null : id));
return (
<SafeAreaView>
<ScrollView>
{data &&
data.map((item) => {
return (
<View
...
key={item.id}
>
<TouchableOpacity
style={{ padding: 10 }}
onPress={expandHandler(item.id)} // <-- attach callback and pass id
>
...
</TouchableOpacity>
{item.id === expandId && ( // <-- check if id match expand id
<ExpandComponent />
)}
</View>
);
})}
</ScrollView>
</SafeAreaView>
);
}

How to check FlatList if its null or not

How can I check the FlatList if it's null or not and if null I will display something like No Available Booking's? I have tried the code below using short hand operator but it's not working.
{bookings == null ?
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={styles.title}>No Availabe Booking's Yet!</Text>
</View>
:
<FlatList
data={bookings}
renderItem={flatListItem}
refreshing={refresh}
onRefresh={refreshSummary}
keyExtractor={item => item._id}
/>
}
Instead of making checks on the data and conditionally rendering the FlatList and the empty list view, you can use the existing prop provided by the FlatList i.e. ListEmptyComponent. You can read more about the FlatList and its other props in the official documentation of the React-Native here.
A typical usage of the ListEmptyComponent could be:
import React, { PureComponent } from 'react';
import { Text, View, StyleSheet, FlatList } from 'react-native';
export default class BookingsList extends PureComponent {
state = {
bookings: [
// {
// _id: 1,
// title: 'I am a booking'
// }
],
refreshing: false
};
keyExtractor = (item) => String(item._id);
refreshSummary = () => {};
renderBookings = ({ item }) => (
<View style={styles.bookingCard}>
<Text style={styles.title}>{item.title}</Text>
</View>
);
renderItemSeparatorComponent = () => <View style={styles.separator} />;
//render the empty list component in case the data array for the FlatList is empty
renderListEmptyComponent = () => (
<View style={styles.emptyListContainer}>
<Text style={styles.noBookingsFound}>
No Availabe Booking's Yet!
</Text>
</View>
);
render() {
const { bookings, refreshing } = this.state;
return (
<FlatList
data={bookings}
refreshing={refreshing}
renderItem={this.renderBookings}
onRefresh={this.refreshSummary}
ListEmptyComponent={this.renderListEmptyComponent} //<==== here
ItemSeparatorComponent={this.renderItemSeparatorComponent}
contentContainerStyle={styles.list}
keyExtractor={this.keyExtractor}
/>
);
}
}
const styles = StyleSheet.create({
bookingCard: {
backgroundColor: 'white',
padding: 10,
marginTop: 2,
borderBottomWidth: 0.5
},
title: {
fontSize: 16,
fontWeight: 'bold'
},
emptyListContainer: {
alignItems: 'center',
justifyContent: 'center',
},
noBookingsFound: {
fontSize: 16,
},
separator: {
height: 15
},
list: {
paddingHorizontal: 15,
paddingBottom: 40
}
});
Would booking not be an Array for a flatlist ?
return (
{bookings !== undefined && bookings.length > 0 ?
<View>
<FlatList
data={bookings}
renderItem={flatListItem}
refreshing={refresh}
onRefresh={refreshSummary}
keyExtractor={item => item._id}
/>
</View>
:
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={styles.title}>No Availabe Booking's Yet!</Text>
</View>
}
);
** Edited as I think I missed your point!
You can return a conditional view in React Native as follows
return (
<View>
{state.someVar == null ?
(<ACOMPONENT />)
:
(<ADIFFCOMPONENT />)
}
</View>
);
Hopefully that's a better response.
u can use listemptycomponent
Rendered when the list is empty. Can be a React Component Class, a render function, or a rendered element.
https://reactnative.dev/docs/flatlist#listemptycomponent
<FlatList
data={bookings}
renderItem={flatListItem}
refreshing={refresh}
onRefresh={refreshSummary}
keyExtractor={item => item._id}
ListEmptyComponent={<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={styles.title}>No Availabe Booking's Yet!</Text>
</View>}
/>
{ bookings && Array.isArray(bookings) ? (
<FlatList
data={bookings}
renderItem={flatListItem}
refreshing={refresh}
onRefresh={refreshSummary}
keyExtractor={item => item._id}
/>
) : (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={styles.title}>No Availabe Booking's Yet!</Text>
</View>
)
}

Unexpected token, expected "}" at my countText case

Please bear with me as I am new to all of this and this is my first posted question! I am trying to set up a simple "welcome screen" with a signup button , and am just learning the ios/App development process/ react-native and javascript. Can anyone explain exactly why there is an error at the line 68? I got this error previously and thought it may be because I was "calling"the styles outside of the class, but I believe that is only an issue if this were a function and not a class?
Error Reads: Failed to load bundle(http://localhost:8081/index.bundle?
platform=ios&dev=true&minify=false) with error:(SyntaxError: /Users/name/appname/App.js: Unexpected token, expected “}” (68:13)
type Props = {};
export default class App extends Component<Props> {
render() {
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#F5FCFF"
},
welcome: {
fontSize: 20,
textAlign: "center",
margin: 10
},
instructions: {
textAlign: "center",
color: "#333333",
marginBottom: 10
},
button: {
alignItems: "center",
backgroundColor: "blue",
width: 100,
padding: 10
},
countText: {
padding: 20,
color: "#FF00FF"
}
});
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to P2P Blockchain!</Text>
<Text style={styles.instructions}>To get started, click below!</Text>
<View style={styles.container}>
<TouchableHighlight style={styles.button} onPress={this.onPress}>
<Text> Sign Up Here! </Text>
</TouchableHighlight>
<View style={[styles.countContainer]}>
<Text style={[styles.countText]}>
{this.state.count !== 0 ? this.state.count : null}
</Text>
</View>
</View>
</View>
);
}
}
import React, { Component } from 'react';
import { View, StyleSheet, TouchableHighlight, Text } from 'react-native';
type Props = {};
class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
count: 10,
};
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to P2P Blockchain!</Text>
<Text style={styles.instructions}>To get started, click below!</Text>
<View style={styles.container}>
<TouchableHighlight style={styles.button} onPress={this.onPress}>
<Text> Sign Up Here! </Text>
</TouchableHighlight>
<View style={styles.countContainer}>
<Text style={styles.countText}>
{this.state.count !== 0 ? this.state.count : null}
</Text>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#F5FCFF"
},
welcome: {
fontSize: 20,
textAlign: "center",
margin: 10
},
instructions: {
textAlign: "center",
color: "#333333",
marginBottom: 10
},
button: {
alignItems: "center",
backgroundColor: "blue",
width: 100,
padding: 10
},
countText: {
padding: 20,
color: "#FF00FF"
}
});
export default App;

conditionality render headerRight - React Native

I have to render headerRight conditionally in navigation options.
Right now
static navigationOptions = ({ navigation }) => ({
title: i18N.t('atmbranchpickHeader'),
headerRight: (
<TouchableHighlight
underlayColor="#E22F39"
onPress={() => {
navigation.navigate("home");
}}
>
<Image
style={{ marginRight: 20 }}
source={require('../../resources/toolbar/home_white.png')}
/>
</TouchableHighlight>
),
headerTintColor: "white",
headerStyle: {
backgroundColor: "#E22F39"
// top: 30
}
});
My Component
import React, { Component } from "react";
import {
View,
TextInput,
Text,
TouchableOpacity,
TouchableHighlight,
StyleSheet,
AsyncStorage,
BackHandler,
Image,
FlatList,
Dimensions,
TouchableWithoutFeedback
} from "react-native";
import i18n from "../../i18n/i18n.js";
import { colors } from "../../constants/colors.js";
import Storage from "../../utils/AsyncStorage.js";
class AtmBranchTypeSelect extends Component {
// Render callBack
constructor(props) {
super(props);
this.state = {
data: [
],
stBool: false,
}
}
async componentWillMount() {
BackHandler.addEventListener('hardwareBackPress', () => this.props.navigation.goBack());
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', () => this.props.navigation.goBack());
}
static navigationOptions = ({ navigation }) => ({
title: i18n.t('atmbranchpickHeader'),
headerRight: (
<TouchableHighlight onPress={() => {
navigation.navigate('home');
}}>
<Image style={{ marginRight: 20 }} source={require('../../resources/toolbar/home_white.png')} />
</TouchableHighlight>),
headerTintColor: 'white',
headerStyle: {
backgroundColor: colors.themeColor,
// top: 30
}
});
_renderList = ({ item }) => {
return (
<TouchableWithoutFeedback onPress={(event) => this._selectedItem(item.key)}>
<View style={styles.listRowContainer}>
<View style={styles.listinside1Container}>
<Image style={styles.listImage} source={item.icon} />
<View style={styles.listContainer} onPress={(event) => this._selectedItem(item.text)} >
<Text style={styles.listHeader} >{item.header}</Text>
<Text style={styles.listValue} >{item.value}</Text>
</View>
</View>
<Image style={styles.listimgArrow} source={require('../../resources/toolbar/chevron_right_grey.png')} />
</View>
</TouchableWithoutFeedback>
);
}
// Render callBack
render() {
return (
<View style={styles.mainWrapper} >
<FlatList data={this.state.data} renderItem={this._renderList} />
</View>
);
}
}
const styles = StyleSheet.create({
mainWrapper: {
flex: 1,
height: Dimensions.get('window').height,
width: Dimensions.get('window').width,
flexDirection: 'column',
justifyContent: 'flex-start'
},
listRowContainer: {
flexDirection: 'row',
marginTop: 10,
height: 80,
backgroundColor: '#FFFFFF',
justifyContent: 'space-between',
borderBottomWidth: 1,
borderColor: 'lightgray'
},
listinside1Container: {
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center'
},
listContainer: {
alignItems: 'flex-start',
justifyContent: 'center',
flexDirection: 'column',
backgroundColor: '#FFFFFF',
// borderBottomWidth: 1,
// borderColor: 'lightgray'
},
listHeader: {
color: 'black',
fontFamily: 'Roboto-Medium',
marginLeft: 10,
fontSize: 18,
},
listValue: {
fontFamily: 'Roboto-Regular',
marginTop: 4,
color: 'black',
marginLeft: 10,
fontSize: 14,
},
listImage: {
alignSelf: 'center',
height: 25,
width: 25,
margin: 10
},
listimgArrow: {
// flex: 1,
// flexDirection:'row',
alignSelf: 'center',
height: 25,
width: 25,
margin: 10
},
listVal: {
borderWidth: 1,
borderRadius: 10,
color: 'darkgreen',
borderColor: 'white',
backgroundColor: 'white',
fontWeight: 'bold'
},
});
export default AtmBranchTypeSelect;
From the code I have, headerRight will be displayed in all scenarios. consider I have a scenario like based on state value I have to enable/disable headerRight Button .
for example this.state.stBool? headerRight:(.....) : null
I have to render in this way.Please guide me to achieve this.
You could nest the navigation options inside the render and toggle it based on the state value. Haven't tested and not positively on performace. Hope it helps.
import React, { Component } from "react";
import {
View,
TextInput,
Text,
TouchableOpacity,
TouchableHighlight,
StyleSheet,
AsyncStorage,
BackHandler,
Image,
FlatList,
Dimensions,
TouchableWithoutFeedback
} from "react-native";
import i18n from "../../i18n/i18n.js";
import { colors } from "../../constants/colors.js";
import Storage from "../../utils/AsyncStorage.js";
class AtmBranchTypeSelect extends Component {
// Render callBack
constructor(props) {
super(props);
this.state = {
data: [],
stBool: false
};
}
async componentWillMount() {
BackHandler.addEventListener("hardwareBackPress", () =>
this.props.navigation.goBack()
);
}
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", () =>
this.props.navigation.goBack()
);
}
_renderList = ({ item }) => {
return (
<TouchableWithoutFeedback onPress={event => this._selectedItem(item.key)}>
<View style={styles.listRowContainer}>
<View style={styles.listinside1Container}>
<Image style={styles.listImage} source={item.icon} />
<View
style={styles.listContainer}
onPress={event => this._selectedItem(item.text)}
>
<Text style={styles.listHeader}>{item.header}</Text>
<Text style={styles.listValue}>{item.value}</Text>
</View>
</View>
<Image
style={styles.listimgArrow}
source={require("../../resources/toolbar/chevron_right_grey.png")}
/>
</View>
</TouchableWithoutFeedback>
);
};
// Render callBack
render() {
const { stBool } = this.state;
const navigationOptions = ({ navigation }) => ({
title: i18n.t("atmbranchpickHeader"),
headerRight: stBool ? (
<TouchableHighlight
onPress={() => {
navigation.navigate("home");
}}
>
<Image
style={{ marginRight: 20 }}
source={require("../../resources/toolbar/home_white.png")}
/>
</TouchableHighlight>
) : null,
headerTintColor: "white",
headerStyle: {
backgroundColor: colors.themeColor
// top: 30
}
});
return (
<View style={styles.mainWrapper}>
<FlatList data={this.state.data} renderItem={this._renderList} />
</View>
);
}
}

Resources