So basically I'm creating a quiz component, and I need to loop through all the questions, but display only one question at a time, and after the question is answered, display another one.
How could I do that? I would normally use for loop, but it is not recommended, how can I achieve the same outcome with .map(), or any other function?
This is basically what I want to happen:
for(let i = 0; i < quiz.questions.length(); i++) {
return (
<Content>
<View style={styles.gutter}>
<View style={styles.container}>
<View style={styles.content}>
<Text>{quiz.questions[i].question}</Text>
</View>
<Button
primary
label={quiz.answers.option1}
onPress={() => {
quiz.answers.option1 === quiz.questions[i].rightAnswer
? continue // would continue work here as expected?
: console.log("Wrong");
}}></Button>
<Button
primary
label={quiz.answers.option2}
onPress={() => {
quiz.answers.option2 === quiz.questions[i].rightAnswer
? continue // would continue work here as expected?
: console.log("Wrong");
}}></Button>
<Button
primary
label={quiz.answers.option3}
onPress={() => {
quiz.answers.option3 === quiz.questions[i].rightAnswer
? continue // would continue work here as expected?
: console.log("Wrong");
}}></Button>
</View>
</View>
</Content>
);
}
You should create a functional component with useState hook:
import React, { useState } from 'react';
function Quiz() {
const [index, setIndex] = useState(1);
const current = quiz.questions[index];
return (
<Content>
<View style={styles.gutter}>
<View style={styles.container}>
<View style={styles.content}>
<Text>{current.question}</Text>
</View>
<Button
primary
label={quiz.answers.option1}
onPress={() => {
if (quiz.answers.option1 === current.rightAnswer) {
setIndex(index + 1);
} else {
console.log('Wrong');
}
}}
/>
<Button
primary
label={quiz.answers.option2}
onPress={() => {
if (quiz.answers.option2 === current.rightAnswer) {
setIndex(index + 1);
} else {
console.log('Wrong');
}
}}
/>
<Button
primary
label={quiz.answers.option3}
onPress={() => {
if (quiz.answers.option3 === current.rightAnswer) {
setIndex(index + 1);
} else {
console.log('Wrong');
}
}}
/>
</View>
</View>
</Content>
);
}
If you want to show only one answer you should write a code like this:
I think you wanted to load one item already and you got undefined error message, so that's why you are asking your question
Let's assume you are getting your data via props of redux(it can be anything like regular state)
Render(){
<div>
{this.props.mydata &&
this.props.mydata.quiz[0] &&
this.props.mydata.quiz[0].question
}
</div>
}
This code checks your data, whenever the data loaded you can use it and you won't get any error. And you need to make it whenever your player answered right it goes +1 in array.
I hope you got the idea and works for you
Related
Im trying to map my userForm state to render each by each , but the map function sometimes returns me as undefiend and sometimes as an react infinite loop and I'm not finding a way to solve it, somene could help me ?
my code:
const FormScreen = async({route}) => {
const [userForm, setuserForm] = useState([]);
if (userForm.length > 0) {
console.log(userForm,'campos:',userForm.fields);
return;
} else {
setuserForm(await JSON.parse(route.params.paramKey));
}
{...}
return (
<SafeAreaView style={{flex: 1}}>
<View style={styles.container}>
<Text style={styles.textStyle}>
COLLECTION :
</Text>
{userForm.map((item) => (
<Text keys={item.fields}>test</Text>
))}
</View>
</SafeAreaView>
);
};
Move setuserForm(await JSON.parse(route.params.paramKey)); into a useEffect hook.
https://reactjs.org/docs/hooks-effect.html
This is my function for rendering items in a flatlist
renderItem = ({ item }) => {
var integer = Number(item.key)
return (
<View>
<Text style={styles.row}>
{item.text}
</Text>
<View style={{flexDirection:'row'}}>
{this.createButtonYes(integer)}
{this.createButtonNo(integer)}
{this.answer(this.state.buttonStates[integer])}
</View>
<Text >__</Text>
</View>
)
}
And the problem I am facing is the function this.answer is not being called when the state of buttonStates changes
answer = (val) => {
if(val){
return(
<Text style={{fontSize:20}}>YES</Text>
)
}else if(val==false){
return(
<Text style={{fontSize:20}}>NO</Text>
)
}else{
return(
<Text style={{fontSize:20}}>Not Answered</Text>
)
}
}
I assumed that every time the state changes the function would be called but that does not seem to be the case, so does anyone have a solution? What I want is whenever the buttons are pressed the state will change and then this.answer will take the changed state and display what it has to accordingly.
Thanks
EDIT:
Code for the button:
buttonYesHelp = num =>{
const newItems = [...this.state.buttonStates];
newItems[num] = true;
return newItems
}
createButtonYes = (num) => {
return(
<TouchableOpacity style={styles.buttonYes}
onPress =
{
()=> {{this.setState({ buttonStates:this.buttonYesHelp(num) })}}
}>
<Text style={styles.buttonTextStyle}>YES</Text>
</TouchableOpacity>
)
}
num is the index of the thing I want to change in the list
EDIT:
I have tried multiple different things but the problem I keep running into is that when I render the button I want it to react to a state variable but it never seems to change based on the state even when the state is changing.
For example, in this.answer I assumed that it would return the text based on the state of buttonStates but it seems to only account for the initial state and nothing after
I was able to achieve this in a different piece of code with identical syntax but for some reason this is not working
I'm developing in React Native.
I have a FlatList that renders items. I have added TouchableOpacity and would like to call a function upon press of that item, but the function is not being called.
When I click on the item I get an error of cannot find variable: _onPress
I think it's an issue with scoping. Would someone be able to explain to me what is going wrong please?
I guess a secondary question is: will my _onPress console.log the item name by passing the prop in like I have?
export default class ModalScreen extends React.Component {
//..
_onPress = (item) => {
console.log('Clicked:' + item)
};
renderItem({ item }) {
return (
<TouchableOpacity onPress={() => this._onPress(item)}>
<View>
<Text>{item.name}</Text>
</View>
</TouchableOpacity>
)
}
render() {
return (
//..
<View style={{flex: 2, backgroundColor: '#FFF', flexDirection:'row'}} >
<FlatList
data={this.state.searchedItems}
renderItem={this.renderItem}
/>
</View>
//..
try to change this 'renderItem({ item }) {' with this 'renderItem = ({ item }) => {
I managed to fetch data and show to UI with this code:
export default class BoxGarage extends Component {
render() {
let garage = this.props.garage;
garage.name = garage.name.replace('strtoreplace', 'My Garage');
let cars = garage.cars.length ?
garage.cars.map((val, key) => {
return (
<Car key={key} car={val} />
)
}) : (
<View style={styles.boxEmpty}>
<Text style={styles.textEmpty}>(No Cars)</Text>
</View>
);
return (
<View style={styles.boxGarage}>
<Text>{ garage.name }</Text>
{ cars }
</View>
)
}
}
Then I tried to change with a function, but no cars shown. What is missing?
export default class BoxGarage extends Component {
render() {
let garage = this.props.garage;
garage.name = garage.name.replace('strtoreplace', 'My Garage');
cars = function(garage) {
if (garage.cars.length) {
garage.cars.map((val, key) => {
return (
<Car key={key} car={val} />
);
});
}
else {
return (
<View style={styles.boxEmpty}>
<Text style={styles.textEmpty}>(No Cars)</Text>
</View>
);
}
}
return (
<View style={styles.boxGarage}>
<Text>{ garage.name }</Text>
{ cars(this.props.garage) }
</View>
)
}
}
And I think I should refactor for best practice either using constructor or just move the function outside render, but I don't know what it is. Please advice.
The reason your second code doesn't work is that you're not returning anything from your function if garage.cars.length > 0.
if (garage.cars.length) {
// Added a return statement on the next line
return garage.cars.map((val, key) => {
return (
<Car key={key} car={val} />
);
});
}
That said, i think your first version of the code was much cleaner. If a piece of code got complicated enough that i was tempted to make an inline function to do calculations, i'd either pull that out to another class method, or to another component. In your case though, just doing a ternary or an if/else will be much better.
I currently have two buttons (No, Yes)( component imported from native-base package) that when pressed should update the state with either 0 or 1 respectively and also toggle between true or false to notify if this field has been filled (by default, neither of the two will be pressed, hence set to false).
I have a handleOnClick() function bound to the "No" button with a debugger to test if I actually do hit it, but once inside this function, I'm not sure how to grab any info for associated components (i.e. the "No" text within the Text component) so I can perform logic to check if "No" or "Yes" was pressed.
If this was done in pure React, I know I can access certain data attributes that I add to DOM elements or traverse the DOM, but I'm not sure how to do this in React Native or if I'm able to add custom props to a built in component that I can then access.
class Toggle extends Component {
constructor() {
super()
this.state = {
selectedOption: '',
isFilled: false
}
this.checkField = this.checkField.bind(this)
this.handleOnClick = this.handleOnClick.bind(this)
}
checkField() {
console.log(this)
// debugger
}
handleOnClick(ev) {
debugger
console.log("I was pressed")
}
render() {
const options = this.props.inputInfo.options //=> [0,1]
const optionLabels = this.props.inputInfo.options_labels_en //=>["No","Yes"]
return (
<View style={{flexDirection: 'row'}}>
<View style={styles.row} >
<Button light full onPress={this.handleOnClick}><Text>No</Text></Button>
</View>
<View style={styles.row} >
<Button success full><Text>Yes</Text></Button>
</View>
</View>
)
}
}
If you want to pass information into function, you can pass it when it is called. In your case, you can call your function from arrow function, like so:
<View style={{flexDirection: 'row'}}>
<View style={styles.row} >
<Button light full onPress={() => this.handleOnClick('No')}>
<Text>No</Text>
</Button>
</View>
<View style={styles.row} >
<Button success full><Text>Yes</Text></Button>
</View>
</View>
And in your function
handleOnClick(text) {
debugger
console.log(`${text} pressed`)
}
Have you tried:
render() {
const options = this.props.inputInfo.options //=> [0,1]
const optionLabels = this.props.inputInfo.options_labels_en //=>["No","Yes"]
return (
<View style={{flexDirection: 'row'}}>
<View style={styles.row} >
<Button light full onPress={() => this.handleOnClick('No')}><Text>No</Text></Button>
</View>
<View style={styles.row} >
<Button success full onPress={() => this.handleOnClick('Yes')}><Text>Yes</Text></Button>
</View>
</View>
)
}
and
handleOnClick(word) {
this.setState({ selectedOption: word, isFilled: true })
}