I'm trying to figure out how to use const objects to create 3 categories in my trivia game. I'm defining each one of them like this:
const questions = [
{
question: "Qual o tipo de arquitetura utilizada na Igreja da Madre de Deus?",
answers: [
{ id: "1", text: "Colonial" },
{ id: "2", text: "Maneirista" },
{ id: "3", text: "Gótico" },
{ id: "4", text: "Barroco", correct: true }
]
},
{
question: "No século XIX, o pintor que pintou os painéis da igreja foi:",
answers: [
{ id: "1", text: "Sebastião Canuto da Silva Tavares", correct: true },
{ id: "2", text: "Frans Janszoon Post" },
{ id: "3", text: "Oscar Pereira da Silva" },
{ id: "4", text: "João de Deus Sepúlveda" }
],
quiz_answer: "Esse é o cara!"
}
]
export default questions;
There is a file called IndexQuiz, where I'm calling them through a menu of categories (inside TouchableOpacity):
export default ({ navigation }) => (
<ScrollView>
<SafeAreaView style={{alignItems: "center", flexDirection: "column"}}>
<TouchableOpacity onPress={() =>
navigation.navigate("Quiz", {
title: "Arquitetura",
questions: arquitetura,
color: "rgb(32, 53, 70)"
})}
style={DesafiosStyles.cardContainer}>
</TouchableOpacity>
<TouchableOpacity onPress={() =>
navigation.navigate("Quiz", {
title: "Curiosidades",
questions: curiosidades,
color: "rgb(32, 53, 70)"
})}
style={DesafiosStyles.cardContainer}>
</TouchableOpacity>
<TouchableOpacity onPress={() =>
navigation.navigate("Quiz", {
title: "História",
questions: historia,
color: "rgb(32, 53, 70)"
})}
style={DesafiosStyles.cardContainer}>
</TouchableOpacity>
</SafeAreaView>
</ScrollView>
);
But when it comes to the Quiz file, inside render I have this:
render() {
const questions = this.props.route.params.questions.length;
const question = questions[this.state.activeQuestionIndex];
//console.log(question);
return (
<View
style={[
styles.container,
{ backgroundColor: this.props.route.params.color }
]}
>
<StatusBar barStyle="light-content" />
<SafeAreaView style={styles.safearea}>
<View>
<Text style={styles.text}>{question.question}</Text>
<ButtonContainer>
{answers.map(answer => (
<Button
key={answer.id}
text={answer.text}
onPress={() => this.answer(answer.correct)}
/>
))}
</ButtonContainer>
</View>
<Text style={styles.text}>
{`${this.state.correctCount}/${this.state.totalCount}`}
</Text>
</SafeAreaView>
<Alert
correct={this.state.answerCorrect}
visible={this.state.answered}
/>
</View>
);
}
Every time I try to call any of these categories, I got the "undefined is not an object" exception. I've tried to import the files and the "questions" const itself, but it didn't work. As simple as that must be, I'm stuck in this (yep, I'm kinda slow and basically a newbie with react native lol):
<Text style={styles.text}>{question.question}</Text>
<ButtonContainer>
{answers.map(answer => (
<Button
key={answer.id}
text={answer.text}
onPress={() => this.answer(answer.correct)}
/>
))}
</ButtonContainer>
Without this code, it shows the counter: "this.state.correctCount}/${this.state.totalCount", but that's all. How can I call these questions and answers properly?
I think the problem is that you try to get questions from length property, not from questions object itself:
const questions = this.props.route.params.questions.length;
const question = questions[this.state.activeQuestionIndex];
Related
Context:
In a single screen view, there are 2 dropdowns (made using react-native-autocomplete-dropdown) but only 1 is visible at a time. The other dropdown comes into view when the user toggles the next tab. I am using react hooks and the component is a functional component. Also, I am new to react hooks.
Issue:
When I try to search on the 2nd dropdown, I expect the onChangeText function of the 2nd dropdown to fire but it fires the function of the 1st dropdown.
Source arrays:
const allYards = [{ id: "1", name: "Java" }, { id: "2", name: "React" }, { id: "3", name: "Angular" }];
const [yards, setYards] = useState(allYards);
const allTrucks = [{id: "1", name: "Truck 1"}, {id: "2", name: "Truck 2"}, {id: "3", name: "Truck 3"}]
const [trucks, setTrucks] = useState(allTrucks);
Render View code:
<View style={[styles.cardWrap, { padding: 30 }]}>
{assignTo === 'yard' ?
<>
<View style={[styles.inputGrp, { marginBottom: 30 }]}>
<Text style={[styles.inputLabel]}>Yard</Text>
<AutocompleteDropdown
ref={selectYardRef}
controller={(controller) => {
dropdownController.current = controller
}}
inputHeight={44}
debounce={600}
dataSet={yards.map((m) => ({
id: m.id,
title: m.name,
}))}
onChangeText={searchYards}
useFilter={false}
onSelectItem={(item) => {
console.log('selectedYard', item);
if (item?.id) {
setSelectedYard(item.id);
}
}}
textInputProps={{
placeholder: 'Choose the yard',
style: styles.autoInput,
}}
inputContainerStyle={styles.autoInputContainer}
containerStyle={{}}
rightButtonsContainerStyle={{
backgroundColor: '#fff',
}}
ClearIconComponent={<IconClear />}
ChevronIconComponent={<IconCaret />}
showChevron={true}
onClear={() => {
setSelectedYard(undefined);
}}
onFocus={handleOnFocus}
renderItem={(item, text) => (
<Text style={{ color: '#000', padding: 15 }}>
{`${item.title}`}
</Text>
)}
ItemSeparatorComponent={() => null}
/>
</View>
<View style={[styles.inputGrp]}>
<Text style={[styles.inputLabel]}>Address</Text>
<Text style={[styles.labelValue, { width: '50%' }]}>
123 Name Street
City, ST 00000
</Text>
</View>
</>
:
<View style={[styles.inputGrp, { marginBottom: 30 }]}>
<Text style={[styles.inputLabel]}>Truck</Text>
<AutocompleteDropdown
ref={selectTruckRef}
controller={(controller) => {
dropdownController.current = controller
}}
inputHeight={44}
debounce={600}
dataSet={trucks.map((m) => ({
id: m.id,
title: m.name,
}))}
onChangeText={searchTrucks}
useFilter={false}
onSelectItem={(item) => {
console.log('selectedYard', item);
if (item?.id) {
setSelectedTruck(item.id);
}
}}
textInputProps={{
placeholder: 'Choose the truck',
style: styles.autoInput,
}}
inputContainerStyle={styles.autoInputContainer}
containerStyle={{}}
rightButtonsContainerStyle={{
backgroundColor: '#fff',
}}
ClearIconComponent={<IconClear />}
ChevronIconComponent={<IconCaret />}
showChevron={true}
onClear={() => {
setSelectedTruck(undefined);
}}
onFocus={handleOnFocus}
renderItem={(item, text) => (
<Text style={{ color: '#000', padding: 15 }}>
{`${item.title}`}
</Text>
)}
ItemSeparatorComponent={() => null}
/>
</View>}
</View>
On change handler functions:
const searchTrucks = (searchText: string) => {
console.log("[~]Inside searchTrucks...");
if(searchText){
const regex = new RegExp(searchText, 'i');
const filteredTrucks = trucks.filter(truck => regex.test(truck.name));
setTrucks(filteredTrucks.slice(0, 5)); //Show only 1st 5 matches.
}
else{
setTrucks(allTrucks);
}
}
const searchYards = (searchText: string) => {
console.log("[~]Inside searchYards...");
if(searchText){
const regex = new RegExp(searchText, 'i');
const filteredYards = yards.filter(yard => regex.test(yard.name));
setYards(filteredYards.slice(0, 5)); //Show only 1st 5 matches.
}
else{
setYards(allYards);
}
}
I'm struggling to pass props down into some components. I have an array of data that I'm using but cant access the props to be able to use the data.
I can seem to access the props in a components folder but for some reason I cant access the props from within the same folder.
Any ideas would be very much appreciated. Thanks in advance guys.
const homeOptions = [
{
name: "Create A Beast Workout",
body: "Quick Start Workout",
image: require("../assets/images/ChickSquat.jpg"),
id: "1",
},
{
name: "Beast Workout Diary",
body: "Create and manage your workout shedule",
image: require("../assets/images/DoubleBis.jpg"),
id: "2",
},
{
name: "Pre-Made Workouts",
body: "Use one of our pre-made workouts",
image: require("../assets/images/ChickA.jpg"),
id: "3",
},
{
name: "Statistics",
body: "Analyse your personal statistics",
image: require("../assets/images/WorkoutInProgress.jpg"),
id: "4",
},
{
name: "History",
body: "Keep track of your workout history",
image: require("../assets/images/ChickH.jpg"),
id: "5",
},
];
const HomeScreen = (props) => {
console.log(props);
return (
<View style={Styles.containerTop}>
<View>
<HomeScreenImage style={Styles.top} />
<View style={Styles.top}>
<BigButton title="Beast" />
</View>
</View>
<SafeAreaView style={Styles.flatListContainer}>
<FlatList
data={homeOptions}
renderItem={({ item }) => (
<TouchableOpacity
style={Styles.container}
onPress={() =>
props.navigation.navigate({
routeName: "StackNavigator",
params: {
cardId: props.info.id,
cardImage: props.info.image,
cardName: props.info.name,
cardBody: props.info.body,
},
})
}
>
//////below is where im having the problem accessing props /////////////////
<View style={Styles.cardContainer}>
<Image style={Styles.imageStyle} source={props.info.image} />
<View style={Styles.infoStyle}>
<Text style={Styles.titleStyle}>{props.info.name}</Text>
<Text style={Styles.bodyTextStyle}>{props.info.body}</Text>
</View>
</View>
</TouchableOpacity>
/>
</SafeAreaView>
</View>
);
};
I use flatList to make a list of elements. I would like to show 15 elements and then add a button "see more" to show the next 15 etc.
I was about tu use this tutorial : https://aboutreact.com/react-native-flatlist-pagination-to-load-more-data-dynamically-infinite-list/
But I don't need to use fetch, I already have set up the data (state.listData) and in fact, I'm a little lost on how to adapt it...
I thought that maybe anyone could help me a little.
Thanks a lot
this.state = {
selectedId: '',
setSelectedId:'',
listData:''
}
};
renderItem = ({ item }) => {
const backgroundColor = item.id === this.selectedId ? "transparent" : "fff";
return (
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('UpdateTripsForm')}
style={{ backgroundColor }}
/>
<Image source={require("../../assets/images/arrow.png")} style={{width: 15, height:15, justifyContent: 'center'}}/>
</View>
);
};
initListData = async () => {
let list = await getFlights(0);
if (list) {
this.setState({
listData: list
});
}
};
render() {
return (
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.listData}
renderItem={this.renderItem}
maxToRenderPerBatch={15}
keyExtractor={(item) => item.id}
extraData={this.selectedId}
/>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
{i18n.t("tripsform.action.back")}
</Text>
</View>
<Image
source={require("../../assets/images/btn-background.png")}
style={styles.tripsimg2}
/>
</TouchableOpacity>
</SafeAreaView>
);
};
}
I just tried this thanks to #Pramod 's answer :
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.flightsListitem, style]}>
<Text style={styles.h4}>{item.id}</Text>
</TouchableOpacity>
);
export default class FlightsList extends Component {
constructor(props) {
super(props);
this.state = {
selectedId: '',
setSelectedId:'',
listData:'',
page:1,
perPage:2,
loadMoreVisible:true,
displayArray:[]
}
};
renderItem = ({ item }) => {
const backgroundColor = item.id === this.selectedId ? "transparent" : "fff";
return (
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('UpdateTripsForm')}
style={{ backgroundColor }}
/>
<Image source={require("../../assets/images/arrow.png")} style={{width: 15, height:15, justifyContent: 'center'}}/>
</View>
);
};
initListData = async () => {
let list = await getFlights(0);
if (list) {
this.setState({
listData: list
});
}
};
componentDidMount(){
this.setNewData()
// console.log(tempArray)
}
setNewData(){
var tempArray=[]
if(this.state.listData.length == this.state.displayArray.length){
this.setState({
loadMoreVisible:false
})
}else{
for(var i=0; i<(this.state.page*this.state.perPage); i++){
tempArray.push(this.state.listData)
}
this.setState({
displayArray: tempArray,
loadMoreVisible:true
})
}
}
loadMore(){
this.setState({
page: this.state.page+1
},()=>{
this.setNewData()
})
}
async UNSAFE_componentWillMount() {
this.initListData();
}
render() {
return (
<ImageBackground
source={require("../../assets/images/background.jpg")}
style={styles.backgroundImage}
>
<Header
backgroundImage={require("../../assets/images/bg-header.png")}
backgroundImageStyle={{
resizeMode: "stretch",
}}
centerComponent={{
text: i18n.t("mytrips.title"),
style: styles.headerComponentStyle,
}}
containerStyle={[styles.headerContainerStyle, { marginBottom: 0 }]}
statusBarProps={{ barStyle: "light-content" }}
/>
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.displayArray}
renderItem={this.renderItem}
keyExtractor={(item) => item.id}
extraData={this.selectedId}
/>
{this.state.loadMoreVisible == true?
<Button style={{width:'100%', height:10, backgroundColor:'green', justifyContent:'center', alignItems:'center'}}
title = 'load more'
onPress={()=>{this.loadMore()}}>
</Button>:null}
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
{i18n.t("tripsform.action.back")}
</Text>
</View>
<Image
source={require("../../assets/images/btn-background.png")}
style={styles.tripsimg2}
/>
</TouchableOpacity>
</SafeAreaView>
</ImageBackground>
);
};
}
the flatlist is not displayed : I get :
You can user pagination method with per page limit so that you can have granular control
Load the array per page when component mount
On every click increase the per page and based on per page update data of your flat list
And also put a flag which will check when the data has ended which will help to hide the load more button when data ends
Working example: https://snack.expo.io/#msbot01/suspicious-orange
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
SafeAreaView,
SectionList,
Switch,
FlatList
} from 'react-native';
import Constants from 'expo-constants';
import Icon from 'react-native-vector-icons/FontAwesome';
import AwesomeIcon from 'react-native-vector-icons/FontAwesome';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
page:1,
perPage:2,
loadMoreVisible:true,
DATA: [{
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
title: 'First Item',
},
{
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
title: 'Second Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'Third Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'fourth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'fifth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29sd72',
title: 'sixth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29dr72',
title: 'seventh Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d7w2',
title: 'Eight Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29ad72',
title: 'Nineth Item',
},
{
id: '58694a0f-3da1-471f-bd96-14557d1e29d72',
title: 'Tenth Item',
}],
displayArray:[]
}
}
componentDidMount(){
this.setNewData()
// console.log(tempArray)
}
setNewData(){
var tempArray=[]
if(this.state.DATA.length == this.state.displayArray.length){
this.setState({
loadMoreVisible:false
})
}else{
for(var i=0; i<(this.state.page*this.state.perPage); i++){
tempArray.push(this.state.DATA[i])
}
this.setState({
displayArray: tempArray,
loadMoreVisible:true
})
}
}
loadMore(){
this.setState({
page: this.state.page+1
},()=>{
this.setNewData()
})
}
render() {
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.state.displayArray}
renderItem={({item})=>
<View style={{flexDirection:'row'}}>
<Text style={{fontSize:20}}>{item.title} </Text>
</View>
}
keyExtractor={item => item.id}
/>
{this.state.loadMoreVisible == true?
<View style={{width:'100%', height:10, backgroundColor:'green', justifyContent:'center', alignItems:'center'}} onClick={()=>{this.loadMore()}}>Load more</View>:null
}
</View>
);
}
}
Set data in state (already done ==> this.state.listData)
Set counter in state (initialize with 1)
Set 15 first elements in state (you can name it "renderedData" or something like that) and then increase cuonter to 1
Add a function that increases the "renderedData" by 15 items by increasing the counter by one
Add Footer component to the list which will call the function you created in stage 3
To take only 15( or 30/45/60 etc..) items from the list you can do something like this:
this.setState({ renderedItem: listData.slice(0, counter*15) })
I'm trying to set up a flatlist to test it on a screen in React-native.
When I launch the emulator I'm getting this error : "Can't find variable renderItem" but I don't really know what's wrong with my code. It seemed that I had every element.
Is there someone that can help me ? Thanks a lot for any explanations or answer you can give to help.
import React, { Component } from "react";
import { Header } from "react-native-elements";
import {
FlatList,
SafeAreaView,
StatusBar,
StyleSheet,
Text,
ImageBackground,
View,
ScrollView,
Image,
TouchableOpacity
} from "react-native";
import i18n from "../../src/i18n";
import styles from "../../assets/Styles/Styles";
const DATA = [
{
id: "bd7acbea-c1b1-46c2-aed5-1",
title: "Test 1",
},
{
id: "3ac68afc-c605-48d3-a4f8-2",
title: "Test 2",
},
{
id: "58694a0f-3da1-471f-bd96-3",
title: "Test 3",
},
{
id: "58694a0f-3da1-471f-bd96-4",
title: "Test 4",
},
{
id: "58694a0f-3da1-471f-bd96-5",
title: "Test 5",
},
{
id: "58694a0f-3da1-471f-bd96-6",
title: "Test 6",
},
{
id: "58694a0f-3da1-471f-bd96-7",
title: "Test 7",
},
{
id: "58694a0f-3da1-471f-bd96-8",
title: "Test 8",
},
{
id: "58694a0f-3da1-471f-bd96-9",
title: "Test 9",
},
];
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.flightsListitem, style]}>
<Text style={styles.h3}>{item.title}</Text>
</TouchableOpacity>
);
export default class FlightsList extends Component {
constructor(props) {
super(props);
this.state = {
selectedId: '',
setSelectedId:''
}
};
renderItem = ({ item }) => {
const backgroundColor = item.id === selectedId ? "transparent" : "fff";
return (
<Item
item={item}
onPress={() => setSelectedId(item.id)}
style={{ backgroundColor }}
/>
);
};
render() {
return (
<ImageBackground
source={require("../../assets/images/background.jpg")}
style={styles.backgroundImage}
>
<Header
backgroundImage={require("../../assets/images/bg-header.png")}
backgroundImageStyle={{
resizeMode: "stretch",
}}
centerComponent={{
text: i18n.t("mytrips.title"),
style: styles.headerComponentStyle,
}}
containerStyle={[styles.headerContainerStyle, { marginBottom: 0 }]}
statusBarProps={{ barStyle: "light-content" }}
/>
<ScrollView style={styles.containerScrollNoMargins}>
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
/>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
{i18n.t("tripsform.action.back")}
</Text>
</View>
<Image
source={require("../../assets/images/btn-background.png")}
style={styles.tripsimg2}
/>
</TouchableOpacity>
</SafeAreaView>
<Text>{"\n"}</Text>
</ScrollView>
</ImageBackground>
);
};
}
change
renderItem={renderItem}
to
renderItem={this.renderItem}
You must use renderItem={this.renderItem}
Because your renderItem function is not a variable or inner function of your render function. So, you must use this when you're trying to call another function of your class.
I have item that are in array. I want to change the line after setting the first item and so on. I am getting the values in a row. But I want it in column. I am getting value as:
015245088 9823178404 9851108404
But I want value as:
015245088
9823178404
9823178404
I have implemented as follows:
this.state = {
contact: [
{
id: 0,
name: '015245088'
},
{
id: 1,
name: '9823178404'
},
{
id: 2,
name: '9851108404'
}
]
}
<CardSection>
<FontAwesomeIcon style={styles.contentStyle} icon={faPhone} />
{
this.state.contact.map((item, index) => (
<TouchableOpacity
key={item.id}
style={styles.opacityStyle}
onPress={()=>Linking.openURL(`tel:${item.name}`)}>
<Text style={styles.contactStyle}>{item.name} </Text>
</TouchableOpacity>
))
}
</CardSection>
this.state = {
contact: [
{
id: 0,
name: '015245088'
},
{
id: 1,
name: '9823178404'
},
{
id: 2,
name: '9851108404'
}
]
}
<CardSection>
<FontAwesomeIcon style={styles.contentStyle} icon={faPhone} />
<View style={{flexDirection:'column'}}>
{
this.state.contact.map((item, index) => (
<TouchableOpacity
key={item.id}
style={styles.opacityStyle}
onPress={()=>Linking.openURL(`tel:${item.name}`)}>
<Text style={styles.contactStyle}>{item.name} </Text>
</TouchableOpacity>
}
</View>
</CardSection>
You need to change the flexDirection to column.
So, Just add the following styles to styles.contentStyle. Or wrap your map function with a View and add the following styles to it.
flexDirection: 'column'
flex: 1