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
Related
I have a component, GameGrid which makes an axios call to get the data that I pass as props to another component, GameCard. I currently have a Header component that is in the same file as GameGrid. The header has buttons which when clicked, changes the endpoint that the GameGrid is calling. I am trying to figure out a way to separate the two components, Header and GameGrid, but still have the buttons on the header determine which endpoint the GameGrid should call. Also, is the GameGrid the right place to have the data being called?
GameGrid.jsx
import React, { useEffect, useState } from "react";
import AppBar from '#mui/material/AppBar';
import Toolbar from '#mui/material/Toolbar';
import Box from '#mui/material/Box';
import IconButton from '#mui/material/IconButton';
import Grid from '#mui/material/Grid';
import axios from "axios";
import GameCard from "./GameCard";
export default function GameGrid() {
const [data, setData] = useState(); // Data Hook
const [route, setRoute] = useState('/')
useEffect(() => {
const getData = async () => {
try {
const { data } = await axios.get(`${process.env.REACT_APP_URL}${route}`);
console.log(data);
setData(data);
} catch (err) {
console.log(err);
}
};
getData();
}, [route]);
const createCards = (data) => {
return <GameCard
key = {data.id}
id = {data.id}
url = {data.game_url}
thumbnail = {data.thumbnail}
title = {data.title}
platform = {data.platform}
description = {data.short_description}
genre = {data.genre}
publisher = {data.publisher}
developer = {data.developer}
/>;
}
const Header = () => {
return (
<Box sx ={{ flexGrow: 1 }}>
<AppBar position="fixed">
<Toolbar>
<Grid sx={{ flexGrow: 1,
border: '1px solid white'}} container spacing={2}>
<Grid sx={{
}}>
<IconButton onClick={() => setRoute('/')}>
<img src='computer-mouse.png' alt='Logo' id='icon'/>
</IconButton>
<IconButton onClick={() => setRoute('/shooter')}>
<img src="shooter.png" alt="Shooter Games" />
</IconButton>
</Grid>
<Grid>
</Grid>
</Grid>
</Toolbar>
</AppBar>
</Box>
)
}
return (
<>
<Header/>
<div className="card-container">
{data?.map(createCards)}
</div>
</>
)
}
GameCard.jsx
import React from "react";
import Card from "#mui/material/Card";
import CardContent from "#mui/material/CardContent";
import CardMedia from "#mui/material/CardMedia";
import Typography from "#mui/material/Typography";
import { CardActionArea, Chip } from "#mui/material";
import Stack from '#mui/material/Stack';
export default function GameCard(props) {
return (
<Card sx={{ width: 550, margin: 2}} key={props.id} id={props.id} >
<CardActionArea href={props.url}>
<CardMedia
component="img"
height="fit-content"
image={props?.thumbnail}
/>
<CardContent>
<Stack direction="row" justifyContent="space-between">
<Typography gutterBottom variant="h6" component="div">
{props.title}
</Typography>
<Chip label={props.platform} size="small"/>
</Stack>
<Typography gutterBottom variant="body1" id="description-text"
color="text.secondary"
sx={{
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap'
}}>
{props.description}
</Typography>
<Stack direction="row" justifyContent="space-between">
<Chip label={props.genre} size="small" />
<Chip label={props.publisher} size="small" />
<Chip label={props.developer} size="small" />
</Stack>
</CardContent>
</CardActionArea>
</Card>
);
}
To separate Header, simply define it with all the props it requires. In this instance, that appears to be the setRoute function though you can call it whatever you like.
For example
// Header.jsx
const Header = ({ onRouteClick }) => (
<Box sx={{ flexGrow: 1 }}>
{/* ... */}
<IconButton onClick={() => onRouteClick("/")}>
<img src="computer-mouse.png" alt="Logo" id="icon" />
</IconButton>
<IconButton onClick={() => onRouteClick("/shooter")}>
<img src="shooter.png" alt="Shooter Games" />
</IconButton>
{/* ... */}
</Box>
);
export default Header;
and in your GameGrid component, import Header from "./Header" and use...
<Header onRouteClick={setRoute} />
If you were using Typescript (highly recommended), the API for Header would look something like this
interface HeaderProps {
onRouteClick: (route: string) => void;
}
const Header = ({ onRouteClick }: HeaderProps) => (
// ...
);
I'm having a problem, and I don't really know how to fix this. Been at this for a while now.
I get this typeError with mongoDB:
utils\db.js (38:16) # Object.convertDocToObj
TypeError: Cannot read property '_id' of null
36 |
37 | function convertDocToObj(doc) {
> 38 | doc._id = doc._id.toString();
| ^
39 | doc.createdAt = doc.createdAt.toString();
40 | doc.updatedAt = doc.updatedAt.toString();
41 | return doc;
This my db.js
import mongoose from 'mongoose';
const connection = {};
async function connect() {
if (connection.isConnected) {
console.log('already connected');
return;
}
if (mongoose.connections.length > 0) {
connection.isConnected = mongoose.connections[0].readyState;
if (connection.isConnected === 1) {
console.log('use previous connection');
return;
}
await mongoose.disconnect();
}
const db = await mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log('new connection');
connection.isConnected = db.connections[0].readyState;
}
async function disconnect() {
if (connection.isConnected) {
if (process.env.NODE_ENV === 'production') {
await mongoose.disconnect();
connection.isConnected = false;
} else {
console.log('not disconnected');
}
}
}
function convertDocToObj(doc) {
doc._id = doc._id.toString();
doc.createdAt = doc.createdAt.toString();
doc.updatedAt = doc.updatedAt.toString();
return doc;
}
const db = { connect, disconnect, convertDocToObj };
export default db;
This is my [slug].js
import React from 'react'
import useStyles from '../../utils/styles'
import NextLink from 'next/link'
import { Link, Grid, List, ListItem, Typography, Card, Button } from '#material-ui/core'
import Image from 'next/image'
import Layout from '../../components/Layout'
import Product from '../../models/Product'
import db from '../../utils/db';
export default function ProductScreen(props) {
const { product } = props
const classes = useStyles()
if(!product) {
return <div>Product Not Found</div>
}
return (
<Layout title={product.name} description={product.description}>
<div className={classes.section}>
<NextLink href="/" passHref>
<Link>Back to Products</Link>
</NextLink>
</div>
<Grid container spacing={1}>
<Grid item md={6} xs={12}>
<Image src={product.image} alt={product.name} width={640} height={640} layout="responsive"/>
</Grid>
<Grid item md={3} xs={12}>
<List>
<ListItem>
<Typography component="h1" variant='h1'>
{product.name}
</Typography>
</ListItem>
<ListItem>
<Typography>
Category: {product.category}
</Typography>
</ListItem>
<ListItem>
<Typography>
Brand: {product.brand}
</Typography>
</ListItem>
<ListItem>
<Typography>
Rating: {product.rating} stars ({product.numReviews} reviews)
</Typography>
</ListItem>
<ListItem>
<Typography>Description: {product.description}</Typography>
</ListItem>
</List>
</Grid>
<Grid item md={3} xs={12}>
<Card>
<List>
<ListItem>
<Grid container>
<Grid item xs={6}><Typography>Price</Typography></Grid>
<Grid item xs={6}><Typography>${product.price}</Typography></Grid>
</Grid>
</ListItem>
<ListItem>
<Grid container>
<Grid item xs={6}><Typography>Status</Typography></Grid>
<Grid item xs={6}><Typography>{product.countInStock>0?'In stock':'Unavailable'}</Typography></Grid>
</Grid>
</ListItem>
<ListItem>
<Button fullWidth variant="contained" color='primary'>
Add to Cart
</Button>
</ListItem>
</List>
</Card>
</Grid>
</Grid>
</Layout>
)
}
export async function getServerSideProps(context) {
const { params } = context;
const { slug } = params;
await db.connect();
const product = await Product.findOne({ slug }).lean();
await db.disconnect();
return {
props: {
product: db.convertDocToObj(product),
},
};
}
This is my index.js
import Layout from '../components/Layout'
import NextLink from 'next/link'
import { Grid, Card, CardActionArea, CardMedia, CardContent, CardActions, Button, Typography } from '#material-ui/core';
import Product from '../models/Product';
import db from '../utils/db';
export default function Home(props) {
const {products} = props;
return (
<Layout>
<div>
<h1>Products</h1>
<Grid container spacing={3}>
{data.products.map((product) => (
<Grid item md={4} key={product.name}>
<Card>
<NextLink href={`/product/${product.slug}`} passHref>
<CardActionArea>
<CardMedia
component="img"
image={product.image}
title={product.name}
></CardMedia>
<CardContent>
<Typography>
{product.name}
</Typography>
</CardContent>
</CardActionArea>
</NextLink>
<CardActions>
<Typography>${product.price}</Typography>
<Button size='small' color='primary'>
Add to Cart...
</Button>
</CardActions>
</Card>
</Grid>
))}
</Grid>
</div>
</Layout>
)
}
export async function getServerSideProps() {
await db.connect();
const products = await Product.find({}).lean();
await db.disconnect()
return {
props: {
products: products.map(db.convertDocToObj),
},
}
}
And here is my index.js api
import nc from 'next-connect';
import Product from '../../../models/Product';
import db from '../../../utils/db';
const handler = nc();
handler.get(async (req, res) => {
await db.connect();
const products = await Product.find({});
await db.disconnect();
res.send(products);
});
export default handler;
If you are using the lean() method, you won't have the id. The lean() method disables the hydration process which is converting the original result from POJO to Mongoose document. This means that the results you have are in POJO which doesn't include the id.
Try removing the lean() and see if that helps.
Also, you can use mongoose-lean-id module to include the id in the results while using lean(), I personally never used it.
Problem is in this code:
import React from 'react';
import { Card, CardActions, CardContent, CardMedia, Button, Typography, ButtonBase } from '#material-ui/core/';
import DeleteIcon from '#material-ui/icons/Delete';
import MoreHorizIcon from '#material-ui/icons/MoreHoriz';
import { useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { deleteBook } from '../../../../actions/books';
import useStyles from "./styles";
const Book = (book, setCurrentId) => {
const dispatch = useDispatch();
const classes = useStyles();
const user = JSON.parse(localStorage.getItem('profile'));
const history = useHistory();
console.log(book);
const openBook = (e) => {
history.push(`/books/${book._id}`);
};
return (
<Card className={classes.card} raised elevation={6}>
<ButtonBase
component="span"
name="test"
className={classes.cardAction}
onClick={openBook}
>
<CardMedia className={classes.media} image={book.slika || 'https://user-images.githubusercontent.com/194400/49531010-48dad180-f8b1-11e8-8d89-1e61320e1d82.png'} title={book.ime_knjige} />
{(user!= null && user.result.admin_stranice === true) && (
<div className={classes.overlay2} name="edit">
<Button
onClick={(e) => {
e.stopPropagation();
setCurrentId(book._id);
}}
style={{ color: 'white' }}
size="small"
>
<MoreHorizIcon fontSize="default" />
</Button>
</div>
)}
<Typography className={classes.title} gutterBottom variant="h5" component="h2">{book.ime_knjige}</Typography>
<Typography className={classes.title} gutterBottom variant="h5" component="h2">{book.autor_knjige}</Typography>
<CardContent>
<Typography variant="body2" color="textSecondary" component="p">{book.opis_knjige && book.opis_knjige.split(' ').splice(0, 20).join(' ')}...</Typography>
<Typography variant="body2" color="textSecondary" component="p> {book.rejting}</Typography>
</CardContent>
<div className={classes.details}>
<Typography variant="body2" color="textSecondary" component="h2 {book.zanr && book.zanr.map((zanr) => `#${zanr} `)}</Typography>
</div>
</ButtonBase>
<CardActions className={classes.cardActions}>
{(user && user.result.admin_stranice === true) && (
<Button size="small" color="secondary" onClick={() => dispatch(deleteBook(book._id))}>
<DeleteIcon fontSize="small" /> Delete
</Button>
)}
</CardActions>
</Card>
);
};
export default Book;
The object "book" i pass here is defined and have all the atributes needed and their values. I used console.log to see object in console and it's there, with everything it should have. But when i try to access any of atributes the JavaScript sees them as undefined for some reason. Some idea why it does that?
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>
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