How to do conditional rendering in react native with more than 1 condition?
Following is some portion of my code
Index
.then(response => response.json())
.then((responseData) => {
this.setState({
progressData:responseData,
});
.....
......
render() {
const { progressData }= this.state;
return(
<View style={{flex:1}}>
<HeaderExample />
<View>
{progressData == "1"}
(<View>
<Text style={{fontSize:28,color:"#8470ff",fontWeight: 'bold',paddingTop:20,alignSelf:'center'}}>Pending</Text>
</View>)}
{ progressData == "2" &&
(<View>
<CardSection>
<Text style={{fontSize:28,color:"#8470ff",fontWeight: 'bold',paddingTop:20,alignSelf:'center'}}>InProgress </Text>
<View style={styles.buttonContainer}>
<Button
title="Report"
color="#8470ff"
onPress={() =>onPressReport()}
/>
</View>)}
But here it is for a single case means if responseData contains only one field. But now the reponseData contains 2 arrays. Each with 3 objects. So how do I check conditional rendering here?My responseData looks like this. I want to populate some UI on each condition. That means if status = 1 && work_type ="plumber" then render some UI.
Also if status = 2 && work_type="electrical" && assigend_to="worker_45" then render some ui. So how do I do this?
Please help
You can move your render in a new variable, or function. to keep clear the render function
render() {
const { progressData }= this.state;
return(
<View style={{flex:1}}>
<HeaderExample />
<View>
{renderProgressData(progressData)}
... //rest of your code
)
}
and in your renderProgressData function you can create a switch
renderProgressData = (progress) => {
switch(progress) {
case 1:
return (<View>1</View>)
case 2:
return (<View>1</View>)
// ... and so on
default:
return (<View>Default View</View>)
}
}
It is a little cleaner in this way for me.
Related
I am building a small quiz in react native. On my screen, I want the user to chose several correct answers from a choice of 4-6 options. I build a custom checkbox for that. If the correct answers are checked (and all wrong answers are unchecked) the user should get a message that the answer was correct.
Here is the custom checkbox component. I only included the code for three boxes to make the code a bit shorter:
import { TouchableOpacity, StyleSheet, View, Text } from "react-native";
import { FontAwesome } from "#expo/vector-icons";
function AnswerContainer_CheckBox(props) {
const [userInput, setUserInput] = useState("");
const answerHandler = () => {
if (userInput == props.finalAnswer) {
dispatch(answerTrue());
}
};
const [checked_1, setChecked_1] = useState(false);
const [checked_2, setChecked_2] = useState(false);
const [checked_3, setChecked_3] = useState(false);
/*Visibility
if set to false via props, the checkbox won't show
*/
const [box_1_Visibility, setBox_1_Visibility] = useState(
props.box_1_Visibility
);
const [box_2_Visibility, setBox_2_Visibility] = useState(
props.box_2_Visibility
);
const [box_3_Visibility, setBox_3_Visibility] = useState(
props.box_3_Visibility
);
/* Functions to chech and uncheck ityems*/
const checkedHandler_1 = () => {
if (checked_1 == false) {
setChecked_1(true);
setUserInput(userInput + props.box_1_Letter);
answerHandler();
} else {
setChecked_1(false);
setUserInput(userInput.replace(props.box_1_Letter,""));
answerHandler();
}
};
const checkedHandler_2 = () => {
if (checked_2 == false) {
setChecked_2(true);
setUserInput(userInput + props.box_2_Letter);
answerHandler();
} else {
setChecked_2(false);
setUserInput(userInput.replace(props.box_2_Letter,""));
answerHandler();
}
};
const checkedHandler_3 = () => {
if (checked_3 == false) {
setChecked_3(true);
setUserInput(userInput + props.box_3_Letter);
answerHandler();
} else {
setChecked_3(false);
setUserInput(userInput.replace(props.box_4_Letter,""));
answerHandler();
}
};
return (
<View>
{/* Checkbox 1 */}
<TouchableOpacity
onPress={() => {
checkedHandler_1();
}}
>
<View
style={
(box_1_Visibility === true && styles.mainContainer) || styles.hide
}
>
<View style={styles.icon}>
<FontAwesome
name={checked_1 == true ? "check-square" : "square-o"}
size={24}
color={checked_1 == true ? "#3787FF" : "#BFD3E5"}
/>
</View>
<Text style={styles.checkButtonText}>{props.box_1_Label}</Text>
</View>
</TouchableOpacity>
{/* Checkbox 2 */}
<TouchableOpacity
onPress={() => {
checkedHandler_2();
}}
>
<View
style={
(box_2_Visibility === true && styles.mainContainer) || styles.hide
}
>
<View style={styles.icon}>
<FontAwesome
name={checked_2 == true ? "check-square" : "square-o"}
size={24}
color={checked_2 == true ? "#3787FF" : "#BFD3E5"}
/>
</View>
<Text style={styles.checkButtonText}>{props.box_2_Label}</Text>
</View>
</TouchableOpacity>
{/* Checkbox 3 */}
<TouchableOpacity
onPress={() => {
checkedHandler_3();
}}
>
<View
style={
(box_3_Visibility === true && styles.mainContainer) || styles.hide
}
>
<View style={styles.icon}>
<FontAwesome
name={checked_3 == true ? "check-square" : "square-o"}
size={24}
color={checked_3 == true ? "#3787FF" : "#BFD3E5"}
/>
</View>
<Text style={styles.checkButtonText}>{props.box_3_Label}</Text>
</View>
</TouchableOpacity>
</View>
);
}
So what is happening here: onPress the checkedHandler-function checks the state. If "false" it will change it to "true". If it is "true" it will change to "false". Depending on the state, the style of the checkbox will change. The checkedHandler-function will also update the string within "userInput" depending on the state. The content of the string is catched via props from the parent component ("box_1_Letter" etc).
This is how I added the component in my screen/parent component:
<AnswerContainer_CheckBox
finalAnswer={"AC"}
box_1_Visibility={true}
box_2_Visibility={true}
box_3_Visibility={true}
box_4_Visibility={true}
box_5_Visibility={false}
box_6_Visibility={false}
box_1_Label={"Shanghai"}
box_1_Letter={"A"}
box_2_Label={"Paris"}
box_2_Letter={"B"}
box_3_Label={"New York"}
box_3_Letter={"C"}
box_4_Label={"Berlin"}
box_4_Letter={"D"}
/>
As you can see I first define how many boxes should be visible and I also add a label to each box and the associated "letter". "finalAnswer" contains the correct answer.
Now comes my problem: let's say in my example "Shanghai" and "New York" are the correct answers. Both boxes have to be checked while all other boxes have to be unchecked. How do I check that within the parent component/Screen?. My solution does not work. The user would have to check the boxes in the right order (and even then it somehow didn't work). The solution would also only be available within the component, not the parent. I do not want to create a global state with redux for this.
Any help appreciated (be aware: I am pretty new to this :-)
this modified answerHandler should solve your problem
const answerHandler = () => {
// if input length equals result length
if (userInput.length === props.finalAnswer.length) {
const inclusionMap = Array.from(props.finalAnswer).map((char) => {
return userInput.includes(char);
});
// if all the characters are included
if (inclusionMap.every((bool) => bool === true)) {
dispatch(answerTrue());
}
}
};
I'm trying to create list with an altered style based on selection:
I have a dynamic state -
constructor(props) {
super(props);
this.state = {
inventoryArray: InvData.map(obj => ({...obj, clicked: false})),
}
}
I want to use the clicked state of each object to determine styling - but am not quite sure on th syntax to use - i've tried:
returnInventoryObjects = () => {
return (
this.state.inventoryArray.map((data, i) => {
return (
if({data.clicked}) ? {
<View key={i} style={[InventoryStyles.InventoryItem, InventoryStyles.InventoryItemSel ]}>
}
else{
<View key={i} style={[InventoryStyles.InventoryItem]}>
}
which doesn't work -
I've also tried an inline dynamic style ie -
returnInventoryObjects = () => {
return (
this.state.inventoryArray.map((data, i) => {
return (
<View key={i} style={[InventoryStyles.InventoryItem, {data.clicked} && InventoryStyles.InventoryItemSel ]}>
Can anyone please advise how to achieve this please?
Heres the error shown when implementing option 2 -
Styles used are:
InventoryItem:{
backgroundColor:'rgba(255,255,255,0.8)',
paddingVertical:8,
paddingHorizontal:8,
flexDirection:'row',
marginBottom:15,
},
InventoryItemSel:{
backgroundColor:'rgba(255,255,255,0.2)',
},
I think you tried to make conditional style like so:
<View
key={i}
style={
data.clicked
? [InventoryStyles.InventoryItem]
: [InventoryStyles.InventoryItem, InventoryStyles.InventoryItemSel]
}
/>
I would do:
// More maintanable
const DEFAULT = [InventoryStyles.InventoryItem];
<View
style={
data.clicked ? DEFAULT : [...DEFAULT, InventoryStyles.InventoryItemSel]
}
/>
Notice that the statement {data.clicked} is parsed as an expression within scope, and not as an object or something else that you meant.
So using it within an array or in if expression, is an syntax error.
{ // Scope
data.clicked // Expression
}
Please try this:
this.state.inventoryArray.map((data, i) => {
if(data.clicked) {
return (
<View key={i} style={[InventoryStyles.InventoryItem, InventoryStyles.InventoryItemSel ]} />
)
}
else {
return (
<View key={i} style={[InventoryStyles.InventoryItem]} />
)
}
}
React components that don't have children need to be self-closing (/ at the end). Notice the difference between
<View key={i} style={[InventoryStyles.InventoryItem, InventoryStyles.InventoryItemSel ]}>
and
<View key={i} style={[InventoryStyles.InventoryItem, InventoryStyles.InventoryItemSel ]} />
Be sure to correct this in all your View components
The rest looks fine!
How do I get rid of this warning? I know I need to get rid of setState functions in the render method, but I need them, so where should I put them?
export default class List<T> extends React.PureComponent<ListProps<T>> {
state = { wrapped: false, iconName: "arrow-down" };
render(): React.Node {
const { rows, renderRow, title, onPress } = this.props;
if (this.state.wrapped === true) {
list = undefined;
this.setState({ iconName: "arrow-up" });
} else {
list = rows.map((row, index) => (
<View key={index} style={index !== rows.length - 1 ? styles.separator : {}}>
{renderRow(row, index)}
</View>
));
this.setState({ iconName: "arrow-down" });
}
return (
<TouchableWithoutFeedback>
<View style={styles.container}>
<View style={[styles.separator, styles.relative]}>
<Text style={styles.title}>{title}</Text>
<IconButton
style={styles.icon}
onPress={() => this.setState({ wrapped: !this.state.wrapped })}
name={this.state.iconName}
color="black"
/>
</View>
{list}
</View>
</TouchableWithoutFeedback>
);
}}
No, you don't need to get rid of setState calls in your render method in general. You just need to put them so that they are not called in each render call (by binding them to user events like clicks for example) and thereby trigger another re-render, that again calls setState and again re-renders and so on.
So in your particular case, you are firing setState right in the beginning in the if() { ... } else { ... } statements. No matter what this.state.wrapped is, you end up at setState.
Here is a possible solution for how you might want to change your code specifically to make it what I assume you want it to make:
export default class List<T> extends React.PureComponent<ListProps<T>> {
state = { wrapped: false };
render(): React.Node {
const { rows, renderRow, title, onPress } = this.props;
const { wrapped } = this.state;
return (
<TouchableWithoutFeedback>
<View style={styles.container}>
<View style={[styles.separator, styles.relative]}>
<Text style={styles.title}>{title}</Text>
<IconButton
style={styles.icon}
onPress={() => this.setState({ wrapped: !wrapped })}
name={wrapped ? "arrow-up" : "arrow-down"}
color="black"
/>
</View>
{!wrapped && (
<View key={index} style={index !== rows.length - 1 ? styles.separator : {}}>
{renderRow(row, index)}
</View>
)}
</View>
</TouchableWithoutFeedback>
);
}}
Because the value of your icon is directly correlated to wrapped, you don't need to specifically set the icon in the state. Rather infer it from wrapped.
I am mapping over array of objects in react-native:
const status_image = [
{
image: require("../../assets/claimsImages/Question.png")
},
{
image: require("../../assets/claimsImages/Approved.png")
},
{
image: require("../../assets/claimsImages/In Process.png")
},
{
image: require("../../assets/claimsImages/Cancelled.png")
}
]
this.state = {
newdata: [],
images: status_image,
}
render(){
var img = this.state.images.map( val => val.image);
const claimData = this.state.newdata.map((val, index) => {
return (
<View key={index} style={{flexDirection: 'row', justifyContent: 'space-between', margin: 15,}}>
<View style={{ borderBottomWidth: 1,borderBottomColor: '#A6ACAF' }}>
<Text>EW</Text>
{val.claim_approval_status.map((status, i) => (
<View style={{marginVertical: 12}}>
<Image
style={styles.stretch}
source={img[i]}
/>
</View>
))}
</View>
<View>
{val.claim_approval_status.map((status, i) => (
<View style={{marginTop: 36}}>
<Text>{status}</Text>
</View>
))}
</View>
</View>
)
});
return (
<View>
{claimData}
</View>
)
}
What I want to achieve is the images should be mapped on the basis of status value i.e. if my val.claim_approval_status equals 'question', then the image 'Question.png' should be rendered in front of that status. If the val.claim_approval_status equals 'approved', then image be 'Approved.png'.
All these status values are mapped all together in a table along with the images, just the images need to appear as per their status values.
I tried following switch case approach likewise, but got confused implementing.
componentDidMount(){
this.state.newdata.map((x, i) => {
x.claim_approval_status.map((y, i) => {
let status = y;
console.log(status) //this gives four status values
'question, approved, inprocess,
cancelled'
});
});
var status_values = this.state.images.map((val, i) =>
{
switch (i) {
case 1:
// something to append that image in status data mapping
break;
case 2:
// something to append that image in status data mapping
break;
case 3:
// something to append that image in status data mapping
break;
}
}
// ...
);
}
How can I achieve the result? Any help appreciated.
Instead of switch case, you can apply simplest approach, by assigning object key as the status code, something like below:
const status_image =
{
question: require("../../assets/claimsImages/question.png"),
approved: require("../../assets/claimsImages/approved.png"),
inprocess: require("../../assets/claimsImages/inprocess.png"),
cancelled: require("../../assets/claimsImages/cancelled.png")
};
and once you have the response you can just use
<Image
style={styles.stretch}
source={status_image[status]}
/>
I'm trying to make a game with react native and I want to show a different options when i change the picker value.
basically when I select the first option on the picker a component has to appear and when I select the second one another component.
I tried this function but not working
pickerOptionText = () => {
if (this.state.PickerValueHolder==this.state.filter[0]) {
return (
<Text>{instructions[2]}</Text>
);
}else {
return (
<Text>{instructions[1]}</Text>
);
}
return null;
}
here is my code
export default class Facil extends Component {
constructor(props)
{
super(props);
this.state = {
isLoading: true,
PickerValueHolder : '',
filter: [
{
"option":"Palabras por categoria"
},
{
"option":"Palabras por caracteres"
}
],
dataSource:[]
}
}
componentDidMount() {
return fetch(API_URL)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson
})
})
.catch((error) => {
console.error(error);
});
}
render() {
const resizeMode = 'stretch';
pickerOptionText = () => {
if (this.state.PickerValueHolder==this.state.filter[0]) {
return (
<Text>{instructions[2]}</Text>
);
}else {
return (
<Text>{instructions[1]}</Text>
);
}
return null;
}
return (
<View style={styles.container}>
<Image source={require('../../Images/HomeLayout.png')}
style={styles.imagen}
/>
<View style={styles.mView}>
<View style={styles.panel}>
<Text style={styles.titlePanel}>MODO FACIL</Text>
<Text style={styles.instructions}>{instructions[0]}</Text>
<View style={styles.picker}>
<Picker
selectedValue={this.state.PickerValueHolder}
style={ {height: '100%',width: '100%'}}
mode="dropdown"
onValueChange={(itemValue, itemIndex) => this.setState({PickerValueHolder: itemValue})} >
{ this.state.filter.map((item, key)=>(
<Picker.Item label={item.option} value={item.option} key={key} />)
)}
</Picker>
</View>
<View style={styles.gameOpt}>
<Text>[dynamic options]</Text>
{pickerOptionText}
</View>
</View>
</View>
<TouchableOpacity style={styles.button}><Text style={styles.btnText}>Play!</Text></TouchableOpacity>
</View>
);
}
}
You forgot '()'.
pickerOptionText is a function, not a React component.
<Text>[dynamic options]</Text>
{pickerOptionText}
to:
<Text>[dynamic options]</Text>
{pickerOptionText()}
You can try using Conditional Rendering of JSX, by this you can use ternary operator and a simple if condition. this is written as:
{this.state.PickerValueHolder==this.state.filter[0] ?
<Text>{instructions[2]}</Text>
:<Text>{instructions[1]}</Text>
}
and if you need simple if condition then,
{ condition == true && <Text>your text here</Text>
}