how do I check for an empty object in javascript? - javascript

so I have a response from an API like this,
How do I check the empty object from the API inverse?
I have tried using lodash to check it but this did not work well in react native, I have not studied it further
this is my state
const [product, setProduct] = useState([])
const [loading, setLoading] = useState(true)
const getProduct = () => {
api.get(`/v1/snack/product/nearby?idKecamatan=${dataKecamatan.idKecamatan}`,
{ headers: { 'Authorization': 'Bearer' + AsyncStorage.getItem('app-token') } }
)
.then(res => {
setProduct(res.data)
setLoading(!loading)
console.log(res.data)
})
}
useEffect(() => {
navigation.addListener('focus', () => {
getProduct()
})
}, [navigation])
<View>
{Object.keys(product.Data).length === 0 ? (
<Text>data from 'Data' is empty</Text>
) : (
<View>
<Text>Data not empty</Text>
</View>
)}
</View>
if Data is not empty
{
"Data": {
"data": [
{
"idSnack": 1,
"codeSnack": "MCA69825829",
"nameSnack": "Taro",
"imageSnack": "localhost/snack-upload/public/media/product/130720xMDVDa_8hrNIx.jpg",
"price": "16500.00",
"rating": 0,
"productSold": 0,
"favStatus": 1,
"idAgen": 2
}
],
"metadata": {
"total": 2,
"count": 2,
"per_page": 20,
"current_page": 1,
"total_pages": 1,
"prev_page": "",
"next_page": ""
}
},
"Message": "SUCCESS"
}
if the data is blank the response is like this:
{
"Data": {},
"Message": "Tidak ada snack disekitarmu"
}
I want if data is empty to return like this
<View>
{Object.keys(product.Data).length === 0 ? (
<Text>gk ada</Text>
) : (
<View>
<Text>ada</Text>
</View>
)}
</View>

You check if Data.data exists -->
if (Data.data) {
// show data
} else {
// handle null response
}
Alternatively, you can check the length of Data keys: Object.keys(Data).length

Related

How to avoid Uncaught (in promise) Error: Too many re-renders in my case

I am using React as the frontend and Flask as the backend.
Today, the frontend needs to use the response from the backend in the user interface, which will be stored as a dialog using setDialog and rendered in the UI.
However, an error "Uncaught (in promise) Error: Too many re-renders. React limits the number of renders to prevent an infinite loop" keeps occurring.
I have tried using useEffect to fetch the dialog and also setting up a button to avoid repeated rendering, but neither method has worked.
Using useEffect:
const [dialog, setDialog] = useState([]);
useEffect(() => {
const handleAddDialog = async () => {
const url = `http://127.0.0.1:5000/question_hints_dialog/ww/dd/C1_P1`;
const response = await fetch(url);
const data = await response.json();
console.log("data", data);
setDialog(data);
};
handleAddDialog();
}, []);
Using button mode:
const handleAddDialog = async () => {
const url = `http://127.0.0.1:5000/question_hints_dialog/ww/dd/C1_P1`;
const response = await fetch(url);
dialogs = await response.json();
setDialog(dialogs)
};
return(
<Button onClick={()=>handleAddDialog()}>Start</Button>
)
I would like to know how to solve this issue. Thank you.
<List ref = {(ref)=>setScrollbarRef(ref)} className={classes.messageArea} style={{maxHeight: 500, overflow: 'auto'}}>
<Button onClick={()=>handleAddDialog()}>開始</Button>
{dialog && dialog.map((msg, idx) => {
console.log("detail",msg.detail)
let linkComponent = null;
if(msg.id === 1){
linkComponent =<></>;
}
else if (msg.id === 2) {
setHintsCount(1)
linkComponent = importConcept
//<Link href="#" onClick={() => handleProcessStage(false, "開始 PyTutor")}>開始 PyTutor</Link>;
} else if (msg.id === 3) {
linkComponent = <Link href="#" onClick={() => handleConcept(false)}>GOGo</Link>;
}
const detail_update = <>{msg.detail}<br/>{linkComponent}</>
return (
<React.Fragment key={idx}>
<ListItem key={idx} className = {msg.from === 'student'? classes.stuPos:classes.tutorPos}>
{msg.detail && (
<Grid container className = {msg.from === 'student'?classes.stuMsg:classes.tutorMsg}>
<Grid item={true} xs style={{display:'flex'}}>
<ListItemText primary= {
detail_update
}/>
</Grid>
<Grid item={true} xs={12}>
<ListItemText className={msg.from === 'student'? classes.stuPos:classes.tutorPos} secondary={currentTime}></ListItemText>
</Grid>
</Grid>
)}
</ListItem>
</React.Fragment>
);
})}
</List>
Here is now my frontend useEffect code:
useEffect(() => {
const fetchData = async () => {
const options = await getStoredOptions();
setOptions(options);
setOptionsLoaded(true);
};
const handleScrollbar = () => {
if (scrollbarRef) {
new PerfectScrollbar(scrollbarRef, {
wheelSpeed: 2,
wheelPropagation: true,
minScrollbarLength: 20
});
}
};
if (!optionsLoaded) {
fetchData();
}
handleScrollbar();
if (hint) {
console.log("Hint updated: ", hint);
}
if (optionsLoaded && options?.student_name && options?.student_id) {
console.log("initial");
setIsNew(true);
// do something here...
setIsNew(false);
}
}, [scrollbarRef, isSolved, optionsLoaded, hint, pesudo, cloze, originCode, advCode, count, options]);
Backend code:
#app.route('/question_hints_dialog/<string:stu_name>/<string:stu_id>/<string:hint_id>')
def generate_question_hints_dialog(stu_name, stu_id, hint_id):
name = userInfo.student_name
stu_id =userInfo.sudent_id
dialog = []
# dialog.append({"id": 1, "detail": f"... {stu_name} ... {stu_id}", "from": 'student'})
dialog.append({"id": 1, "detail": f"...,{stu_name}! ... " , "from": 'tutor' })
dialog.append({"id": 2, "detail": f"...", "from": 'tutor'})
dialog.append({"id": 3, "detail": "..." , "from": 'tutor' })
dialog.append({"id": 4, "detail": "..." , "from": 'tutor' })
dialog.append({"id": 5, "detail": "..." , "from": 'tutor' })
dialog.append({"id": 6, "detail": "..." , "from": 'tutor' })
dialog.append({"id": 7, "detail": "..." , "from": 'tutor' })
dialog.append({"id": 8, "detail": "..." , "from": 'tutor' })
return jsonify(dialog)
I tried many method to solve this issue but they couldn't work.
Finally, I found that
const [dialog, setDialog] = useState<{ id: number; detail?: JSX.Element; from: string }[]>([]);
The problem is that detail is initialized as JSX.Element
When React reloads, it would keep to set detail as JSX.Element, but JSX keeps changing. So, the re-render problem happens.
Now I change to
const [dialog, setDialog] = useState<{ id: number; detail: string; from: string }[]>([]);
and it figures out.
Share here and thanks for your concern.
If anything I realize wrong, feel free to let me know.

Mapping JSON to MUI cards not returning any UI elements or errors

I have the following JSON which I want to map to MUI Cards. I am not getting any error messages but nothing is being displayed. The console.log(questionGroups) only displays the JSON after changing some unrelated code to cause a live reload.
const [questionGroups, setQuestionGroups] = useState("");
const fetchQuestionGroups= async () => {
setQuestionGroups(
await fetch(`API_LINK`).then((response) => response.json())
);
console.log(questionGroups);
};
useEffect(() => {
fetchQuestionGroups();
}, []);
...
<Box className={classes.cards}>
{questionGroups?.displaygroups?.IntakeQuestion?.map((group, groupIndex) => {
return (
<Card className={classes.card1}>
<CardHeader title={group.GroupName} />
</Card>
);
})}
</Box>
This is a sample of my JSON:
{
"displaygroups": {
"IntakeQuestions": [
{
"GroupId": 11,
"GroupName": "Group 1",
"SizingId": null,
"OwnerName": "Jane Doe",
"Questions": 0,
"Answered": null,
"Validated": null
}
]
}
}
Use && instead of ?
<Box className={classes.cards}>
{questionGroups &&
questionGroups.displaygroups &&
questionGroups.displaygroups.IntakeQuestions.map((group, groupIndex) => {
return (
<Card className={classes.card1}>
<CardHeader title={group.GroupName} />
</Card>
);
})}
</Box>
You need to set the state once the data is available.
const fetchQuestionGroups= async () => {
const data = await fetch(`API_LINK`)
const temp = response.json()
setQuestionGroups(temp);
console.log(questionGroups);
};

Why is code being executed before forEach loop finishes?

In the code below, I'm expecting the forEach loop to execute, then initialize the selectedGroup state with data created from the loop.
Upon render of the component, the loop works just fine, but the selectedGroup state doesn't get initialized. It seems that selectedGroup is trying to be initialized before the loop finishes. Any ideas?
Code in Question:
const MenuContent = ({ items, modifiers }) => {
let groups = [];
items.forEach((item) => {
!groups.includes(item.group) && groups.push(item.group);
});
const [selectedGroup, setSelectedGroup] = useState(groups[0]);
return (
<View style={styles.container}>
<Text>{selectedGroup}</Text>
</View>
);
};
Items Array Used in Code:
Array [
Object {
"86": false,
"description ": null,
"group": "Liquors",
"ingredients ": null,
"modifiers": Array [
"gh5OdUAQC0bCr9O0HrF8",
],
"name": "Smirnoff",
"price": 350,
},
Object {
"86": false,
"description": "Vodka, Passionfruit, Ginger, Lemon",
"group": "Cocktails",
"ingredients ": null,
"modifiers": Array [
"hzxrMy17xrikXA0yumYB",
"sLrkn3QXOgOzbHobdIRG",
],
"name": "Passionfruit Mule",
"price": 1400,
},
]
You could do with useEffect hook
const MenuContent = ({ items, modifiers }) => {
const [selectedGroup, setSelectedGroup] = useState(null);
useEffect(()=>{
let groups = [];
items.forEach((item) => {
!groups.includes(item.group) && groups.push(item.group);
});
setSelectedGroup(groups[0])
},[items])
return (
<View style={styles.container}>
<Text>{selectedGroup}</Text>
</View>
);
};
if you need to prevent the render until value get assigned. use with && conditional operator on render
return (
<View style={styles.container}>
{selectedGroup && <Text>{selectedGroup}</Text>}
</View>
)

How to Generate dynaimc UI on JSON response react-native

// Here I am trying to generate Dynamic UI . Through API I am getting some response in JSON format , I have to draw that on UI . Like am not defining any "TextInput" it should generate dynamically.Below is some sample JSON response .That may change on different request .
Please help , I am stuck below is just one code, I don't know how to do that .
import * as React from 'react';
import { Text, View, StyleSheet, Alert, Picker } from 'react-native';
import { Constants } from 'react-native';
import { TextInput } from 'react-native-gesture-handler';
var myloop = [];
export default class UiDynamic extends React.Component {
// add a selectValue to your state to stop the overwriting
state = {
PickerValueHolder: [],
selectedValue: ''
}
componentDidMount() {
// remove the return
fetch('http://userapi/inventory/viewinventorytype', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"username" :"admin",
"password" :"admin"
})
}).then((response) => response.json())
.then((responseJson) => {
// use the inventoryTypeData as it is already an array
let PickerValueHolder = responseJson.inventoryTypeData;
for (let i = 0; i < PickerValueHolder.length; i++) {
datavalue=() => {
<Text>Hello ABhi</Text>
console.log("Hello ABhi");
}
}
this.setState({ PickerValueHolder }); // Set the new state
})
.catch((error) => {
console.error(error);
});
}
GetPickerSelectedItemValue=()=>{
Alert.alert(this.state.PickerValueHolder);
}
render() {
for (let i = 0; i < 10; i++) {
datavalue=() => {
<Text>Hello ABhi</Text>
console.log("Hello ABhi");
}
myloop.push(
<View key={<TextInput></TextInput>}>
<Text style={{ textAlign: 'center', marginTop: 5 }} >{<Text>Hello</Text>}</Text>
</View>
);
}
return (
<View style={styles.container}>
{this.datavalue.bind(this)}
{myloop}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
}
});
// Below is Sample JSON Response ..
Suppose here am getting four attributes and their datatype , So on UI 4 "TextInput" should generate .
{
"inventoryTypeData": [{
"data type": int,
"Field 1": ""
}, {
"data type": string,
"Field2": ""
}, {
"data type": int,
"Field 3": ""
}, {
"datatype": int,
"Field4": ""
}],
"success": "true"
}
import { TextInput } from 'react-native';
Once you have mapped your response to state
this.setState({ PickerValueHolder });
You can then loop through it in your render method with the map function
return(
<View style={styles.container}>
{this.state.PickerValueHolder.map(data=>(
<View>
<TextInput
placeholder={data.placeholder}
keyboardType={data.datatype==="int" ? 'numeric': 'default'}
onChangeText={(text) => this.setState({...this.state.value [data.name]: text })}
value={this.state.value[data.name]}
/>
<Button
onPress={this.handleButtonPress(data.name).bind(this)}
/>
<View/>
/>
))}
</View>
)
}```
So here you are checking if the datatype is an int and then setting the keyboard type to numeric
This is also setting all the changes to a value object in state.
The Button calls a function with the name of of the value related to the text input

React-Native returning objects from nested array

I am new to React-Native and struggling to return objects from a nested array (Hopefully I am using the correct terminology).
I am grabbing my data from the tfl tube status api JSON is below:
[
{
"$type": "Tfl.Api.Presentation.Entities.Line,
Tfl.Api.Presentation.Entities",
"id": "bakerloo",
"name": "Bakerloo",
"modeName": "tube",
"disruptions": [],
"created": "2018-03-13T13:40:58.76Z",
"modified": "2018-03-13T13:40:58.76Z",
"lineStatuses": [
{
"$type": "Tfl.Api.Presentation.Entities.LineStatus,
Tfl.Api.Presentation.Entities",
"id": 0,
"statusSeverity": 10,
"statusSeverityDescription": "Good Service",
"created": "0001-01-01T00:00:00",
"validityPeriods": []
}
],
"routeSections": [],
"serviceTypes": [],
"crowding": {}
},
I am fetching the data using Axios.
state = { lineDetails: [] };
componentDidMount() {
axios.get('https://api.tfl.gov.uk/line/mode/tube/status')
.then(response => this.setState({ lineDetails: response.data }));
};
I am returning the data like this.
renderLineDetails() {
return this.state.lineDetails.map((details) =>
<TubeList
key={details.id}
details={details} />
)};
render() {
return (
<ScrollView>
{this.renderLineDetails()}
</ScrollView>
);
}
My TubeList component looks like:
const TubeList = ({ details }) => {
const { name, statusSeverityDescription } = details;
const { nameStyle, statusStyle } = styles;
return (
<TubeCard>
<CardSectionTitle>
<Text style={nameStyle}>{name}</Text>
</CardSectionTitle>
<CardSectionStatus>
<Text style={statusStyle}>{statusSeverityDescription}</Text>
</CardSectionStatus>
</TubeCard>
);
};
Is someone able to explain why statusSeverityDescription is not displaying in my list below.
Iphone Simulator image
Thank you.
Instead of statusSeverityDescription you have to use lineStatuses and map it for getting statuses.
TubeList:
const TubeList = ({ details }) => {
const { name, lineStatuses } = details;
const { nameStyle, statusStyle } = styles;
return (
<TubeCard>
<CardSectionTitle>
<Text style={nameStyle}>{name}</Text>
</CardSectionTitle>
{lineStatuses.map((status) =>
<CardSectionStatus>
<Text style={statusStyle}>{status.statusSeverityDescription}</Text>
</CardSectionStatus>
}
</TubeCard>
);
};
Thanks for all your comments. I have fixed the issue following Prasun Pal's comments. Below is my new code and screenshot of the working app.
renderLineDetails() {
return this.state.lineDetails.map((details) =>
<TubeList
key={details.id}
lineStatus={details.lineStatuses[0]}
lineName={details}
/>
)};
const TubeList = ({ lineName, lineStatus }) => {
const { statusSeverityDescription } = lineStatus;
const { name } = lineName;
const { nameStyle, statusStyle } = styles;
return (
<TubeCard>
<CardSectionTitle>
<Text style={nameStyle}>{name}</Text>
</CardSectionTitle>
<CardSectionStatus>
<Text style={statusStyle}>{statusSeverityDescription}</Text>
</CardSectionStatus>
</TubeCard>
);
};
iPhone screenshot of working app

Categories