export default function Case() {
const classes = useStyles();
const[data,setData]=useState([])
const getcovid=async()=>{
const api=await fetch('https://api.rootnet.in/covid19-in/stats/latest');
const res=await api.json();
console.log(res.data.summary)
setData(res.data.summary)
}
useEffect(()=>{
getcovid()
},[])
return (
<div>
<Typography variant="subtitle1" gutterBottom>
Material-UI Grid:
</Typography>
{
data&&data.map(item=>{
return(
<Grid container spacing={3}>
<Grid item xs={3}>
<Paper className={classes.paper}>Confirmed Cases: <br/>{item}</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.paper}>xs=3</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.paper}>xs=3</Paper>
</Grid>
</Grid>
)
})
}
</div>
);
}
My api - {
"success": true,
"data": {
"summary": {
"total": 26289290,
"confirmedCasesIndian": 26289242,
"confirmedCasesForeign": 48,
"discharged": 23070365,
"deaths": 295525,
"confirmedButLocationUnidentified": 0
},
But it shows an error - TypeError: data.map is not a function
I have tried both with data&&data.map and only with data.map but still i get same errorenter code here
Your data is an object not an array. You can try to use this instead:
Object.keys(data).map(function(key, item) {
console.log(data[key]);
return(
...
)
});
The API response you posted shows that data.summary is an object, not an array. Objects don't have a map method, but arrays do. Instead of using map you can just access the properties individually, such as {data.total} and {data.deaths}.
Related
I am trying to create a dynamic mui grid element when a button is pressed in a react component. When I tried to create let newGrid = document.createElement('Grid') it does not work as a normal div creation.
Any suggestions?
Current code snippet:
return (
<>
<button onClick={addRow}>Add Row</button>
<Grid container className='v-timeline-container'>
<Grid xs={12} item className="v-timeline-item v-center v-border">
<TextItems />
</Grid>
</Grid>
</>
)
What I am trying to achieve dynamically:
return (
<>
<button onClick={addRow}>Add Row</button>
<Grid container className='v-timeline-container'>
<Grid xs={12} item className="v-timeline-item v-center v-border">
<TextItems />
</Grid>
<Grid xs={12} item className="v-timeline-item v-center v-border">
<TextItems />
</Grid>
</Grid>
</>
)
React uses a VDOM to keep track of stuff internally, you manipulating the DOM directly using DOM APIs is considered dangerous. What you should do this something like this.
function App(){
// you probably want to track the text items in each row,
// so use an array of objects
const [gridItems, setGridItems] = useState([]);
// this function adds a new grid item
const onClick = () => {
setGridItems([...gridItems, {text: "new row", id: gridItems.length + 1}]);
};
return (
<>
<button onClick={onClick}>Add Row</button>
<Grid container className='v-timeline-container'>
{
gridItems.map(item => (
<Grid xs={12} item className="v-timeline-item v-center v-border">
<TextItems>{item.text}</TextItems>
</Grid>
))
}
</Grid>
</>
);
}
Using setState is a solution.
It increments a number in state to create an array from it.
In your case you will certainly push new data to an array when you click on the button. But you get the idea.
import React, { useState } from 'react';
import { Grid } from '#mui/material'
const ListItems = () => {
const [items, setItems] = useState(1);
const addRow = () => {
setItems((prev) => prev + 1)
}
return (
<>
<button onClick={addRow}>Add Row</button>
<Grid container className='v-timeline-container'>
{
[...Array(items).keys()].map((key) => (
<Grid xs={12} item className="v-timeline-item v-center v-border">
<p>{key + 1} row</p>
</Grid>
))
}
</Grid>
</>
)
}
export default ListItems
what my task is I am using a table with two different conditions like in the first table whatever data coming I will show that in the first table and in the second table whatever row I select in the first table that I want to show in the second table as the second table I called select summary so my task is in the first table whatever row I selected in need to how that row in the second table I am using same table component for this for better you can see CodeSandBox link
import React, { useState, useMemo, useEffect } from "react";
import {
Grid,
makeStyles,
CardContent,
Card,
Box
} from "#material-ui/core";
import EnhancedTable from "./EnhancedTable";
const useStyles = makeStyles((theme) => ({
root: {
padding: theme.spacing(0, 2, 2),
},
formGrid: {
padding: theme.spacing(2),
},
cardColor: {
borderColor: "#0bb7a7",
},
}));
function AddToExclusionList() {
const classes = useStyles();
const [sanctionsList, setSanctionsList] = useState([]);
const updateListsRow = ({ index, value, row }, listType) => {
switch (listType) {
case "sanctions":
setSanctionsList((prevState) => {
prevState[index].status = value;
return [...prevState];
});
break;
default:
}
};
return (
<Grid className={classes.root}>
<Grid item xs={12} style={{ textTransform: "capitalize" }}>
<Card className={classes.cardColor} variant="outlined">
<CardContent>
<>
<EnhancedTable
show={true}
step="first"
/>
</>
</CardContent>
</Card>
</Grid>
<Box mt={3}></Box>
<Grid item xs={12} style={{ textTransform: "capitalize" }}>
<Card className={classes.cardColor} variant="outlined">
<CardContent>
<>
<h3>summary table</h3>
<EnhancedTable
checkboxToggle={(rowDetails) => {
updateListsRow(rowDetails, "sanctions");
}}
/>
</>
</CardContent>
</Card>
</Grid>
</Grid>
);
}
export default AddToExclusionList;
CodeSandBox Link
You've achieved your goal very weird! Anyway, based on your code in codesandbox. You need to add a state to AddToExclusionList component, like this:
const [newRows, setNewRows] = useState([]);
const setSummaryRows = (selectedRows) => {
const copy = [...rows];
const filteredRows = copy.filter((x) => selectedRows.includes(x.name));
setNewRows(filteredRows);
};
We need the mentioned state to update the summary table's rows.
Also add rows and setNewRows prop to EnhancedTable and give it rows from out of the component. In addition move rows and createData to App.js. So you should use EnhancedTable in App.js same as bellow:
<Grid className={classes.root}>
<Grid item xs={12} style={{ textTransform: "capitalize" }}>
<Card className={classes.cardColor} variant="outlined">
<CardContent>
<>
<h3>first table</h3>
<EnhancedTable
// passing data for rendering table according condition
step="first"
rows={rows}
setNewRows={(selected) => {
setSummaryRows(selected);
}}
/>
</>
</CardContent>
</Card>
</Grid>
<Box mt={3}></Box>
<Grid item xs={12} style={{ textTransform: "capitalize" }}>
<Card className={classes.cardColor} variant="outlined">
<CardContent>
<>
<h3>summary table</h3>
<EnhancedTable
// trying to pasing selected data
rows={newRows}
setNewRows={() => {}}
/>
</>
</CardContent>
</Card>
</Grid>
</Grid>
And the last part is using useEffect based on selected in EnhancedTable component:
useEffect(() => {
setNewRows(selected);
}, [selected]);
I am facing the error with the following code and there seems to be no fixes that is solving my issue.
componentDidMount() {
axios.get('http://localhost:8080/home')
.then((response) => {
this.setState({
data: response.data
});
})
.catch((err) => {
console.log(err);
});
}
response from server:
{"data":{"count":{"monthly_blog_count":1,"total_blog_count":1,"monthly_poem_count":0,"total_poem_count":0,"monthly_song_count":0,"total_song_count":0,"monthly_graphics_count":1,"total_graphics_count":1},"latest_graphics":{"link":"ganesha.svg"},"latest_blog":{"title":"test","created":"2021-05-08T07:49:50.000Z","name":"Abhishek Banerjee"},"latest_blog_list":[{"title":"test","created":"2021-05-08T07:49:50.000Z","name":"Abhishek Banerjee"}]}}
<Poems data={data} />
Here's the poem component edited as it's not allowing all code:
the base element is card. I took out code from the poem component since it's complaining not enough text. I have protypes validation on the component as well.
const Poems = (props) => {
const {
data: count,
'data.count.total_poem_count': totalPoemCount,
'data.count.monthly_poem_count': monthlyPoemCount,
} = props;
return (
<Card
sx={{ height: '100%' }}
{...props}
>
<Grid item>
<Typography
color="textPrimary"
variant="h3"
>
{ totalPoemCount }
</Typography>
</Grid>
<Typography
sx={{
color: green[900],
mr: 1
}}
variant="body2"
>
{ monthlyPoemCount }
</Typography>
</card>
);
};
Edit
putting { data?.data?.count?.total_poem_count } works like a charm. But the proptypes validation is gone. Can anyone suggest me how to get proptypes as well working.
Best Practice is to use axios package for fetching api.
axios.get("http://localhost:8080/home")
.then((response) => {
this.setState({
data: response.data
});
})
.catch((err) => {
console.log(err);
});
passing data to component
<Poems data={this.state.data} />
child component
const Poems = ({ data }) => {
const dataToRender = data?.data
return (
<Card
sx={{ height: '100%' }}
{...props}
>
<Grid item>
<Typography
color="textPrimary"
variant="h3"
>
{ dataToRender.count.totalPoemCount }
</Typography>
</Grid>
<Typography
sx={{
color: green[900],
mr: 1
}}
variant="body2"
>
{ dataToRender.count.monthlyPoemCount }
</Typography>
</card>
);
};
<
I have a question on how to use this data to map the headers to it's corresponding values and put that on the UI
This is how the data is structured:
{
"data": {
"details": [
{
"address_line_1": "C O Cwtsatotravel",
"address_line_2": "Not Available",
"city_name": "Arlington",
"state_name": "-",
"country_name": "Japan",
"postal_code": "22203",
"phone_number": "7638527755",
}
]
}
}
This is what I am trying to do in react
const profile_info = data?.details;
const profileHeaders = [
'Address1',
'Address2'
'City',
'State',
'Postal Code',
'Country',
'Phone',
];
return (
<Grid
id="top-card"
className={classes.mainContainer}
container
style={{
marginBottom: '4px',
}}
>
{/* <Grid item md={11} lg={11} id="item-card"> */}
<Grid container item>
<Typography variant="subtitle1">
{profile_info.agency_name}
</Typography>
</Grid>
<Grid
container
style={{
backgroundColor: '#f9f9f9',
}}
>
{profileHeaders.map((v) => (
<Grid
item
style={{
padding: '0px 4px',
}}
>
<Typography className={classes.profileData} gutterBottom={true}>
{v}
</Typography>
<Typography className={classes.profileData}>
{' '}
{profile_info[v]}
</Typography>
</Grid>
))}
</Grid>
</Grid>
);
When I do this, it's getting me all blank values on the UI for the headers
Please help, thank you !
Encode your headers using the same [as in data] keys:
const headers = {
"address_line_1": "Address1",
"address_line_2": "Address2",
"city_name": "City",
later, you can list it
Object.entries(headers).forEach(([key, value]) => console.log(`${key}: ${value}`));
console.log(data) to see its structure and use const to 'alias' iterable (object with props or array) element:
// choose the right data source - depends on what logged out
// console.log(data);
// if(data) console.log(data.details); //... step by step
// const profile_info = data?.details;
// const profile_info = data?.data.details;
const profile_info = data?.details[0]; // object in array here
render values from both headers and profile_info
Object.entries(headers).forEach(([key, value]) => (
<Grid
key={key}
item
style={{
padding: '0px 4px',
}}
>
<Typography className={classes.profileData} gutterBottom={true}>
{value}
</Typography>
<Typography className={classes.profileData}>
{' ??? use css instead! '}
{profile_info[key]}
</Typography>
</Grid>
or you can do the same using .map (you can use index if required)
Object.keys(headers).map((key, idx) => (
<Element key={key}>
<Name title={headers[key]} />
<Value data={profile_info[key]} />
There is some crucial erros on your code:
const profile_info = data?.details;
Your details are stored on property data.data.details, not data.details
So fix this, first:
const profile_info = data?.data.details;
The items in ProfileHeaders are not mapped like properties in profile_info: you have a profile_info['address_line_1'], but not profile_info['Address1'], wich is what you are trying to do in your component.
To make this work the way you want, you should map title and property correctly.
const profileHeaders = [
{
title: "Address1",
property: "address_line_1"
},
{
title: "Address2",
property: "address_line_2"
},
// map all other properties like above.
]
then you can go for that:
{profileHeaders.map((v) => (
<Grid
item
style={{
padding: '0px 4px',
}}
>
<Typography className={classes.profileData} gutterBottom={true}>
{v.title}
</Typography>
<Typography className={classes.profileData}>
{' '}
{profile_info[v.property]}
</Typography>
</Grid>
))}
I am not checking if profile_info is undefined, but you must do it in your component to avoid errors.
I have defined a route in Main component as:
<Route path="/user/:action?/:name?" component={DataForm} />
MainDataCard component:
var data = [ {name: 'abc", label: "ABC", path:"/assets/data/source1.jpg" },
{name: 'pqr", label: "PQR", path:"/assets/data/source2.jpg" },
{name: 'xyz", label: "XYZ", path:"/assets/data/source3.jpg" },
]
I am iterating over a data array and onClick of each card, I ll be navigating to another component which is DataForm. While pushing, I need to send the selected card object.
{data.map((card, index) => {
return (
<Card
key={index}
className="cardStyle"
onClick={() => {
this.onClickForm(card);
}}
>
<CardContent className="cardContent">
<Grid container>
<Grid item md={12}>
<img src={card.path} alt="card" />
</Grid>
<Grid item md={12}>
<Typography color="textSecondary" gutterBottom>
{card.name}
</Typography>
</Grid>
</Grid>
</CardContent>
</Card>
onClickForm = card => {
const {action} = this.props; // action value can be add/update. Currently action = "add"
this.props.history.push({
pathname: `/user/${action}/${card.label}`,
state: { card: card }
});
};
In DataForm component, its showing that card is undefined. It means the data is not being sent to DataForm component. Why is it so?
Thanks in advance.