I've a list of countries which i would like show some info related to the country. I run a loop in order to show the info for each country. I've created a component for a country.
const CountryCard = ({ info }) => {
const { country, infoForLastDate, path } = info
return (
<Card >
<CardContent>
<Typography variant="h5" component="h2" >
{country.name}
</Typography>
<Flag oneToOne={country.oneToOne} fourToThree={country.fourToThree} />
<Typography variant="body2" >
Confirmed: {infoForLastDate.confirmed}
</Typography>
<Typography variant="body2" component="p">
Recovered: {infoForLastDate.recovered}
</Typography>
<Typography variant="body2" component="p">
Deaths: {infoForLastDate.deaths}
</Typography>
</CardContent>
<CardActions>
<Link href={path}>
See more
</Link>
</CardActions>
</Card>
)
}
export default CountryCard
Also, I've created another component to show the flag related to the country.
import React from 'react'
import { imageUrlFor } from '../lib/image-url'
const Flag = ({ oneToOne, fourToThree }) => {
const url = imageUrlFor(oneToOne.asset._id)
return (
<img src={url} />
)
}
export default Flag
I get the error TypeError: oneToOne is null
i don't know why it is like the flags render twice times. when I debugged, first time oneToOne property has a value, but at the end is run again and is null
Why is it happens?
Edit: Add CountryList component:
const CountryList = ({list}) => {
return (
<Grid container spacing={3}>
{ list.length > 1 && list.map(country => {
const countryWithPath = {
...country,
path: `/country/${country.country.name.toLowerCase().replace(' ', '-').replace('*', '')}`
}
return (
<Grid item xs={12} sm={6} key={country._id} >
<CountryCard info={countryWithPath} />
</Grid>)
})
}
</Grid>
)
}
export default CountryList
Related
I have a menu bar that shows category of my shop. When I click on a category a product list of that category should be shown on my shop component (also I use redux library). It works correctly for first time, but when I click on another category the state changes and the filter products update too but they don't show on my shop component. I must click a button on page to show updated list in shop component. How can I show them immediately after updating?
App.jsx
<Menubar/>
<Routes>
<Route path='/shopping' element={<Shop />}></Route>
</Routes>
Menubar.jsx
export default function MenuBar() {
const products = useSelector(state=>state.products);
const navigate = useNavigate();
const getCategory = (e) => {
let category = e.target.innerText;
dispatch(filterProByCategory(products, category));
navigate('/shopping');
}
return (
<>
<ul>
<li onClick={(e)=>getCategory(e)}>
Fashion
</li>
</ul>
</>
)
}
Shop.jsx
export default function Shop() {
const products = useSelector(state => state.filters.filterProducts);
const [filterProducts, setFilterproducts] = useState(products);
return (
<>
<Grid item xs={12} md={8} dir='rtl'>
<Card sx={{ minWidth: 275 }}>
<CardContent>
{/* */}
<Grid container spacing={1}>
{filterProducts && filterProducts.map((product, index) => (
<Grid item xs={12} md={4}>
<ProductCard product={product} key={index} />
</Grid>
))}
</Grid>
</>
)
}
Just use the direct result of products instead of using it for creating another state variable filteredProducts with useState
export default function Shop() {
const products = useSelector(state=>state.filters.filterProducts);
// const [filterProducts , setFilterproducts] = useState (products);
return (
<>
<Grid item xs={12} md={8} dir='rtl'>
<Card sx={{ minWidth: 275 }}>
<CardContent>
{/* */}
<Grid container spacing={1}>
{products && products.map((product , index)=>(
<Grid item xs={12} md={4}>
<ProductCard product={product} key={index}/>
</Grid>
))}
</Grid>
</>
)
}
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>
);
};
<
this is App js
import React from "react"
import React from "react"
import {Cards , Chart , CountryPicker} from "./Components"
import styles from "./App.module.css"
import {fetchData} from "./api"
class App extends React.Component{
state = {
data : {},
}
async componentDidMount() {
const fetchedData = await fetchData()
this.setState({data : fetchedData})
}
render() {
return (
<div className={styles.container}>
<Cards data={this.state.data}/>
<CountryPicker />
<Chart />
</div>
)
}
}
export default App
'''
and this is Card component
import React from "react"
import {Card, CardContent, Typography, Grid} from '#material-ui/core'
import styles from "./Cards.module.css"
import CountUp from "react-countup"
const Cards = (props) => {
console.log(props)
return (
<div className={styles.container}>
<Grid container spacing={3} justify="center">
<Grid item component={Card}>
<CardContent>
<Typography color="textSecondary" gutterBottom>Infected</Typography>
<Typography variant="h5"><CountUp start={0} end={props.data.confirmed.value} separator="," duration={2} /></Typography>
<Typography color="textSecondary">{new Date(props.data.lastUpdate).toDateString()}</Typography>
<Typography variant="body2">Number of Active cases of Covid-19</Typography>
</CardContent>
</Grid>
<Grid item component={Card}>
<CardContent>
<Typography color="textSecondary" gutterBottom>Recovered</Typography>
<Typography variant="h5"><CountUp start={0} end={props.data.recovered.value} separator="," duration={2} /></Typography>
<Typography color="textSecondary">{new Date(props.data.lastUpdate).toDateString()}</Typography>
<Typography variant="body2">Number of Recoveries from Covid-19</Typography>
</CardContent>
</Grid>
<Grid item component={Card}>
<CardContent>
<Typography color="textSecondary" gutterBottom>Deaths</Typography>
<Typography variant="h5"><CountUp start={0} end={props.data.deaths.value} separator="," duration={2} /></Typography>
<Typography color="textSecondary">{new Date(props.data.lastUpdate).toDateString()}</Typography>
<Typography variant="body2">Number of Deaths caused by Covid-19</Typography>
</CardContent>
</Grid>
</Grid>
</div>
)
}
export default Cards
'''
You can use optional chaining to check if you have data in your object.
So try something like below:-
// add optional chaining for below
props.data.confirmed.value => props.data.confirmed?.value
props.data.recovered.value => props.data.recovered?.value
props.data.deaths.value => props.data.deaths?.value
Because the initial state is undefined. Put your code inside an if statement so it doesn't give an error.
<div className={styles.container}>
if(this.state) {
<Cards data={this.state.data}/>
} else {
<p>Loading data...</p>
}
<CountryPicker />
<Chart />
</div>
API this one https://covid19.mathdro.id/api
Sorry for interrupt, but I freaking out with this issue, almost 2 hours im thinking what the problem.
So, for recored and for confirmed it works fine, but for deaths I have this issue:
Issue photo
import React from 'react';
import {Card, CardContent, Typography, Grid} from '#material-ui/core';
import CountUp from 'react-countup';
import cx from 'classnames';
import styles from './Cards.module.css'
const Cards = ({data: {deaths, confirmed, recovered, lastUpdate } } ) => {
if(!confirmed) {
return 'Loading...'
};
return (
<div className={styles.container}>
<Grid container spacing={3} justify="center">
<Grid item component={Card} xs={12} md={3} className={cx(styles.card, styles.infected)}>
<CardContent>
<Typography color="textSecondary" gutterBottom>Infected</Typography>
<Typography variant="h5">
<CountUp
start={0}
end={confirmed.value}
duration={2.5}
separator=","
/>
</Typography>
<Typography color="textSecondary">{new Date(lastUpdate).toDateString()}</Typography>
<Typography variant="body2">Number of active cases</Typography>
</CardContent>
</Grid>
<Grid item component={Card} xs={12} md={3} className={cx(styles.card, styles.recovered)}>
<CardContent>
<Typography color="textSecondary" gutterBottom>Recovered</Typography>
<Typography variant="h5">
<CountUp
start={0}
end={recovered.value}
duration={2.5}
separator=","
/>
</Typography>
<Typography color="textSecondary">{new Date(lastUpdate).toDateString()}</Typography>
<Typography variant="body2">Number of recoveries from COVID-19</Typography>
</CardContent>
</Grid>
<Grid item component={Card} xs={12} md={3} className={cx(styles.card, styles.deaths)}>
<CardContent>
<Typography color="textSecondary" gutterBottom>Deaths</Typography>
<Typography variant="h5">
<CountUp
start={0}
end={deaths.value}
duration={2.5}
separator=","
/>
</Typography>
<Typography color="textSecondary">{new Date(lastUpdate).toDateString()}</Typography>
<Typography variant="body2">Number of deaths caused by COVID-19</Typography>
</CardContent>
</Grid>
</Grid>
</div>
)
}
export default Cards;
this is my app.js
import React from 'react';
import { Cards, Chart, CountryPicker } from './components';
import styles from './App.module.css';
import { fetchData } from './api';
class App extends React.Component {
state = {
data: {},
}
async componentDidMount() {
const fetchedData = await fetchData();
this.setState({ data: fetchedData });
}
render() {
const {data} = this.state;
return (
<div className={styles.container}>
<Cards data={data}/>
<Chart />
<CountryPicker />
</div>
)
}
}
export default App;
So, I'm try without deaths and it works, but with not.
index.js
import axios from 'axios';
const url = 'https://covid19.mathdro.id/api';
export const fetchData = async () => {
try {
const { data: { confirmed, recovered, death, lastUpdate } } = await axios.get(url);
return {confirmed, recovered, death, lastUpdate};
} catch (error) {
}
}
Thanks for helping me out!
You have missed a "s" (it is deaths not death, according to the API) in your fetch data function.
Update your this part
data: { confirmed, recovered, death, lastUpdate } } = await axios.get(url);
to
data: { confirmed, recovered, deaths, lastUpdate } } = await axios.get(url);
:D