React - Displaying Data from API - reactjs

I am trying to consume an API and display it on my app. It is currently pulling in the data in the console log but I can't seem to access it to display it on my app as it is just stuck on loading... I want to show the data with the number of confirmed cases on the h5 variant of the first card {confirmed.value} But not to sure why it is not showing.
Cards
import React from 'react'
import {Card, CardContent, Typography, Grid} from '#material-ui/core'
import styles from './Cards.module.css'
export default function Cards({data: {confirmed, recovered, deaths}}){
if(!confirmed){
return`Loading...`
}
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">{confirmed.value}</Typography>
<Typography color="textSecondary">REAL DATE</Typography>
<Typography variant="body2">Number of active cases of COVID-19</Typography>
</CardContent>
</Grid>
</Grid>
<Grid container spacing={3} justify="center">
<Grid item component={Card}>
<CardContent>
<Typography color="textSecondary" gutterBottom>Recovered</Typography>
<Typography variant="h5">REAL DATA</Typography>
<Typography color="textSecondary">REAL DATE</Typography>
<Typography variant="body2">Number of Recovered cases of COVID-19</Typography>
</CardContent>
</Grid>
</Grid>
<Grid container spacing={3} justify="center">
<Grid item component={Card}>
<CardContent>
<Typography color="textSecondary" gutterBottom>Deaths</Typography>
<Typography variant="h5">REAL DATA</Typography>
<Typography color="textSecondary">REAL DATE</Typography>
<Typography variant="body2">Number of Death cases of COVID-19</Typography>
</CardContent>
</Grid>
</Grid>
</div>
)
}
App JS
import React, { Component } from 'react'
import {Cards, Chart, CountryPicker} from './components'
import styles from './App.module.css'
import {fetchData} from './api'
export default class App extends Component {
state = {
data: {},
}
async componentDidMount(){
const fetchedData = await fetchData()
console.log(fetchedData);
this.setState({data: fetchedData})
}
render() {
const {data} = this.state;
return (
<div className={styles.container}>
<Cards data={data} />
<CountryPicker/>
<Chart/>
</div>
)
}
}

Try to change the component header to:
export default function Cards({confirmed, recovered, deaths})

Related

setCurrentId(prop) is not a function in reactjs

I am getting this error message in console (Uncaught TypeError: setCurrentId is not a function
at onClick (Post.js:19:1) )
I don't know how to handle this error
This is Child Component Post.js
import { Button, Card,CardActions,CardContent,CardMedia,Typography } from '#material-ui/core';
import React from 'react';
import useStyles from "./styles";
import MoreHorizIcon from '#mui/icons-material/MoreHoriz';
import DeleteIcon from '#mui/icons-material/Delete';
import ThumbUpAltIcon from '#mui/icons-material/ThumbUpAlt';
import moment from 'moment';
const Post= ({post , setCurrentId})=>{
const classes = useStyles();
return(
<Card className={classes.card}>
<CardMedia className={classes.media} image={post.selectedFile} title={post.title}/>
<div className={classes.overlay}>
<Typography variant='h6'>{post.creator}</Typography>
<Typography variant='body2'>{moment(post.createdAt).fromNow()}</Typography>
</div>
<div className={classes.overlay2}>
<Button style={{ color: 'white' }} size="small" onClick={() => setCurrentId(post._id)}><MoreHorizIcon fontSize="default" /></Button>
</div>
<div className={classes.details}>
<Typography variant='body2' color='textSecondary'>{post.tags.map((tag)=>`#${tag} `)}</Typography>
</div>
<CardContent>
<Typography className={classes.title} variant='h5' gutterBottom>{post.message}</Typography>
</CardContent>
<CardActions className={classes.cardActions}>
<Button size = 'small' color='primary' onClick={()=>{}}>
<ThumbUpAltIcon fontSize='small'/>
Like
{post.likeCount}
</Button>
<Button size = 'small' color='primary' onClick={()=>{}}>
<DeleteIcon fontSize='small'/>
Delete
</Button>
</CardActions>
</Card>
)
}
export default Post;
This is parent Component
import React from 'react';
import { Grid, CircularProgress } from '#material-ui/core';
import { useSelector } from 'react-redux';
import Post from './Post/Post';
import useStyles from './styles';
const Posts = ({ setCurrentId }) => {
const posts = useSelector((state) => state.posts);
const classes = useStyles();
return (
!posts.length ? <CircularProgress /> : (
<Grid className={classes.container} container alignItems="stretch" spacing={3}>
{posts.map((post) => (
<Grid key={post._id} item xs={12} sm={6} md={6}>
<Post post={post} setCurrentId={setCurrentId} />
</Grid>
))}
</Grid>
)
);
};
This is main file App.js
import React, { useState, useEffect } from 'react';
import { Container, AppBar, Typography, Grow, Grid } from '#material-ui/core';
import { useDispatch } from 'react-redux';
import Posts from './components/Posts/Posts';
import Form from './components/Form/Form';
import { getPosts } from './actions/posts';
import useStyles from './styles';
import memories from './images/memories.png';
const App = () => {
const [currentId, setCurrentId] = useState(0);
const dispatch = useDispatch();
const classes = useStyles();
useEffect(() => {
dispatch(getPosts());
}, [currentId, dispatch]);
return (
<Container maxWidth="lg">
<AppBar className={classes.appBar} position="static" color="inherit">
<Typography className={classes.heading} variant="h2" align="center">Memories</Typography>
<img className={classes.image} src={memories} alt="icon" height="60" />
</AppBar>
<Grow in>
<Container>
<Grid container justify="space-between" alignItems="stretch" spacing={3}>
<Grid item xs={12} sm={7}>
<Posts setCurrentId={setCurrentId} />
</Grid>
<Grid item xs={12} sm={4}>
<Form currentId={currentId} setCurrentId={setCurrentId} />
</Grid>
</Grid>
</Container>
</Grow>
</Container>
);
};
export default App;
I am trying to debug my react code and expecting something that I missed

How to get a value using formik with a MUI component?

I am using Formik in order to submit an array of components using MUI sliders.
I have created a slider component using the MUI library. You can see that in the code below in the declaration of:
import SliderSchedule from ....
So, I searched an example and I found a way to create and delete components and I copy from them. However now I want to also submit the result. But when I click the button I get no values.
As you can see I have the Comp1 that I declare the array const [sliderSchedule, setSliderSchedule]. Which has to contain values like: [(100, 200), (150, 300), (400, 700), ...].
Then I have the two function addHandeler and deleteHandeler that creates and deletes the components. This works fine.
Then we have the formik and that's the last thing that I have implemented after creating the the Comp2 that it's the "component" that we are creating with the addHandeler and deleteHandeler which creates the slider and a button to eliminate:
As you can see is very simple. And it works fine. But then when I click submit I get no values:
Thank you in advance.
import React from 'react';
import { useDispatch, useSelector } from "react-redux";
import history from "../../../history";
import { nanoid } from "nanoid";
/* MUI */
import { Container, Grid, Typography, Paper, TextField } from '#material-ui/core'
/* MUI edited Components */
import Appbar from '../../../components/Appbar';
import Button from '../../../components/Button';
import Translator from '../../../components/Translator';
import SliderSchedule from '../../../components/SliderSchedule';
/* Formik */
import { Formik, Form } from 'formik';
import * as Yup from "yup";
export default function Slots() {
const dispatch = useDispatch();
const paperStyle = { padding: 20, height: 'auto', width: 'auto', margin: "20px auto", backgroundColor: "#A9A9A9" }
function Comp2({ deleteHandeler, id, props }) {
return (
<Grid container item xs={12}>
<Grid item xs={11}> <SliderSchedule id={id} name={props.values.name} value={props.values.time} onChange={time => props.setFieldValue("time", time.target.value)} renderInput={(props) => <TextField {...props} />} /> </Grid>
<Grid item xs={1}> <Button type="button" style="Close" onClick={deleteHandeler}>CloseIcon</Button> </Grid>
</Grid>
);
}
const formSchema = {
time: []
};
function Comp1() {
const [sliderSchedule, setSliderSchedule] = React.useState([]);
const addHandeler = () => { setSliderSchedule(sliderSchedule => [...sliderSchedule, nanoid()]) };
const deleteHandeler = (removeId) => setSliderSchedule((sliderSchedule) => sliderSchedule.filter((id) => id !== removeId));
return (
<Formik
initialValues={formSchema}
onSubmit={values => {
console.log("Monday", values);
}}
render={props => (
<Form>
<Grid container justifyContent="center">
{sliderSchedule.map((id) => (<Comp2 key={id} id={id} deleteHandeler={() => deleteHandeler(id)} props={props} />))}
<Button variant="outlined" color="info" style="AddSlider" startIcon="AddIcon" onClick={addHandeler}> <Translator page="productSchedule" text="buttonAddSlider"></Translator> </Button>
<Button type="submit" variant="contained" color="primary"> Submit </Button>
</Grid>
</Form>
)}
/>
);
}
return (
<>
<Appbar></Appbar>
<Container>
<Grid container alignItems="flex-end">
{/* LA SUMA DE TOTS ELS xs={x} ha de ser xs={12} */}
<Grid container justifyContent="flex-start" item xs={12}>
<Typography variant="h3"> <Translator page="productSchedule" text="typographyTitle"></Translator> </Typography>
<Typography variant="h3"> 📅 </Typography>
</Grid>
<Grid item xs={12}>
<Paper elevation={10} style={paperStyle}>
<Grid container alignItems="flex-end">
<Grid container justifyContent="center" item xs="auto"> <Typography variant="h3"> <Translator page="productSchedule" text="typographyMonday"></Translator> </Typography> </Grid>
<Grid container justifyContent="center" item xs="auto"> <Comp1 /> </Grid>
</Grid>
</Paper>
</Grid>
</Grid>
</Container>
</>
);
}

TypeError: Cannot read properties of undefined (reading 'name') in React

i am getting the below error
TypeError: Cannot read properties of undefined (reading 'name') in react
data is not coming from the Products.
i am learning the react now and this is my first project so could you please help to sort out this issue.
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
App.js
import React from "react";
import Products from "./Components/Products/Products";
const App = () => {
return (
<div>
<Products />
</div>
);
};
export default App;
Products.jsx
import React from "react";
import { Grid } from "#material-ui/core";
import Product from "./Product/Product";
const products = [
{
id: 1,
name: "Macbook Air",
description: "Apple Macbook Air",
price: "$999",
},
{
id: 2,
name: "Macbook Pro",
description: "Apple Macbook Pro",
price: "$1199",
},
];
const Products = () => {
return (
<main>
<Grid container justify="center" spacing={4}>
{products.map((product) => (
<Grid item key={product.id} xs={12} sm={6} md={4} lg={3}>
<Product />
</Grid>
))}
</Grid>
</main>
);
};
export default Products;
Product.jsx
import React from "react";
import {
Card,
CardMedia,
CardContent,
CardActions,
Typography,
IconButton,
} from "#material-ui/core";
import { AddShoppingCart } from "#material-ui/icons";
import useStyles from "./styles";
const Product = ({ product }) => {
const classes = useStyles();
return (
<Card className={classes.root}>
<CardMedia className={classes.media} image="" title={product.name} />
<CardContent>
<div className={classes.cardContent}>
<Typography variant="h5" gutterBottom>
{product.name}
</Typography>
<Typography variant="h5">{product.price}</Typography>
</div>
<Typography variant="h2" color="textSecondary">
{product.description}
</Typography>
</CardContent>
<CardActions disableSpacing className={classes.cardActions}>
<IconButton aria-label="Add to cart">
<AddShoppingCart />
</IconButton>
</CardActions>
</Card>
);
};
export default Product;
How can i solve the above problem
For reference please find the attached image
You didn't pass the product prop to your <Product/> component, so it's undefined by default, and then you're referencing the property name in the Product component of an undefined prop, therefore you get the error.
Make the following change
const Products = () => {
return (
<main>
<Grid container justify="center" spacing={4}>
{products.map((product) => (
<Grid item key={product.id} xs={12} sm={6} md={4} lg={3}>
<Product product={product}/>
</Grid>
))}
</Grid>
</main>
);
};
It appears you are not passing the product as a prop to you <Product /> component.
Add this to your map:
{products.map((product) => (
<Grid item key={product.id} xs={12} sm={6} md={4} lg={3}>
<Product product={product}/>
</Grid>
))}
need to pass prop to your component
{products.map((product) => (
<Grid item key={product.id} xs={12} sm={6} md={4} lg={3}>
<Product product={product}/>
</Grid>
))}

React Material UI CardMedia issue

I used CardMedia to show images, when I go to home page, it doesn't show the images and have some errors. When I click some links and use 'Go Back' button in Browser, images come back.
I use getInitialProps() to get image url in Home component and pass as a property to child (Catalog) component.
I thought Image url would be received in Home component then CardMedia will render the image receiving from Home component as a prop.
Any thoughts?
export default function Home({ categories }) {
return (
<div>
<Header categories={ categories}/>
<Carousel />
<Catalog categories={ categories}/>
<Footer/>
</div>
)
}
Home.getInitialProps = async () => {
const response = await fetch('http://localhost:1337/categories/')
const categories = await response.json();
return {
categories:categories
}
}
export default function Catalog(props) {
const classes = useStyles();
const cards = props.categories
const server = 'http://localhost:1337'
console.log(cards)
return (
<React.Fragment>
<CssBaseline />
<main>
{/* Hero unit */}
<div className={classes.heroContent}>
<Container maxWidth="sm">
<Typography component="h1" variant="h2" align="center" color="textPrimary" gutterBottom>
Album layout
</Typography>
<Typography variant="h5" align="center" color="textSecondary" paragraph>
Something short and leading about the collection below—its contents, the creator, etc.
Make it short and sweet, but not too short so folks don&apos;t simply skip over it
entirely.
</Typography>
</Container>
</div>
<Container className={classes.cardGrid} maxWidth="md">
{/* End hero unit */}
<Grid container spacing={4}>
{cards.map((card) => (
<Link as={`/products/${card.category}`} href='/products/[bathroom]' key={card.id}>
<Grid item key={card.category} xs={12} sm={6} md={4} >
<Card className={classes.card}>
<CardMedia
className={classes.cardMedia}
image={server+ card.img.url}
category={card.category}
/>
<CardContent className={classes.cardContent}>
<Typography gutterBottom variant="h5" component="h2">
{card.category}
</Typography>
<Typography>
Product description.
</Typography>
</CardContent>
</Card>
</Grid>
</Link>
))}
</Grid>
</Container>
</main>
{/* Footer */}
{/* End footer */}
</React.Fragment>
);
}
It turns out that CSS rendering is a bit different in Next.js.
see this doc https://material-ui.com/guides/server-rendering/

Using Ref inside stateless functional component is not working in React JS

I am developing a React JS Web Application. I am new to react js. Now, I am trying to use Ref inside the stateless functional component to retrieve the input value. I followed some of the solutions I found online.
This is my component
const Login = (props) => {
const {
classes,
width
} = props;
// Flip container to column on mobile screens.
const panelDirection = width === 'xs' ? 'column' : 'row';
let emailInput = null;
let passwordInput = null;
return (
<Grid
container
direction="row"
spacing={0}
justify="center"
alignItems="center"
className={classes.background}
>
<Grid item sm={10} xs={12} className={scss.panel}>
<form className="full-height" action="post">
<Grid direction={panelDirection} container spacing={0}>
<Grid
item
sm={6}
xs={12}
>
<Card className={classNames(scss.card, classes['primary-card'])}>
<CardContent className={scss['signup-content']}>
<img src={logoImage} className={scss['signup-logo']} alt="logo" />
<Typography variant="headline" component="h2" gutterBottom>
Web Portal
</Typography>
</CardContent>
<CardActions>
<Button fullWidth href="/register" color="secondary" variant="raised">Create an account</Button>
</CardActions>
</Card>
</Grid>
<Grid
item
sm={6}
xs={12}
>
<Card className={scss.card}>
<CardContent>
<TextField
ref={(input) => { emailInput = input }}
label="Email Address"
fullWidth
/>
<TextField
ref={(input) => { passwordInput = input }}
label="Password"
fullWidth
margin="normal"
type="password"
/>
</CardContent>
<CardActions className={scss['login-actions']}>
<Button href="/login" color="primary" variant="raised">Login</Button>
<Button href="/forgot-password">Forgot Password</Button>
</CardActions>
</Card>
</Grid>
</Grid>
</form>
</Grid>
</Grid>
);
};
As you can see, I am using ref to retrieve the values of email and password input fields. But, when I run, it is still giving me this error.
Warning: Stateless function components cannot be given refs. Attempts to access this ref will fail.
So, how can I fix my code? How can I use Ref correctly in the stateless function component?
Obviously, I followed this, How can I attach to a stateless component's ref in React?
I tried using the class as well. It is giving me the same error. This is the class version of my component.
class Login extends React.Component {
submitForm = e => {
e.preventDefault();
}
constructor(props)
{
super(props);
this.emailInput = React.createRef();
this.passwordInput = React.createRef();
}
render () {
const { classes, width } = this.props;
// Flip container to column on mobile screens.
const panelDirection = width === 'xs' ? 'column' : 'row';
return (
<Grid
container
direction="row"
spacing={0}
justify="center"
alignItems="center"
className={classes.background}
>
<Grid item sm={10} xs={12} className={scss.panel}>
<form className="full-height" action="post" onSubmit={this.submitForm}>
<Grid direction={panelDirection} container spacing={0}>
<Grid
item
sm={6}
xs={12}
>
<Card className={classNames(scss.card, classes['primary-card'])}>
<CardContent className={scss['signup-content']}>
<img src={logoImage} className={scss['signup-logo']} alt="logo" />
<Typography variant="headline" component="h2" gutterBottom>
Web Portal
</Typography>
</CardContent>
<CardActions>
<Button fullWidth href="/register" color="secondary" variant="raised">Create an account</Button>
</CardActions>
</Card>
</Grid>
<Grid
item
sm={6}
xs={12}
>
<Card className={scss.card}>
<CardContent>
<TextField
ref={this.emailInput}
label="Email Address"
fullWidth
/>
<TextField
ref={this.passwordInput}
label="Password"
fullWidth
margin="normal"
type="password"
/>
</CardContent>
<CardActions className={scss['login-actions']}>
<Button type="submit" color="primary" variant="raised">Login</Button>
<Button href="/forgot-password">Forgot Password</Button>
</CardActions>
</Card>
</Grid>
</Grid>
</form>
</Grid>
</Grid>
)
}
}
Login.propTypes = {
classes: PropTypes.shape({}).isRequired,
width: PropTypes.string.isRequired
};
export default compose(withWidth(), withStyles(themeStyles, { withTheme: true }))(Login);
If you insist in using stateless component (which to me they are great) you should use a callback to retrieve the value of your input:
// Login.js
const Login = (props) => {
const {
classes,
width,
onChange, // <- get the callback here
} = props;
...
return (
...
<TextField
name="email"
onChange={onChange}
label="Email Address"
fullWidth
/>
<TextField
name="password"
onChange={onChange}
label="Password"
fullWidth
margin="normal"
type="password"
/>
...
);
// Somewhere to include Login
class LoginPage extends Component {
...
handleInputChange({ target }) {
...
console.log(target.name, target.value);
}
render (
<Login onChange={this.handleInputChange} ... />
)
}
// Or connect it to Redux
const mapDispatchToProps = dispatch => {
const updateLoginInputValues = ({ target }) => dispatch(updateLoginInputValues(target.name, target.value)));
return {
onChange: updateLoginInputValues,
}
};
const connectedLogin = connect(null, mapDispatchToProps)(Login
The only part that you can improve is basically either handling the values by a state management or directly with React. Other than this you need to address the state at some point and you can't keep all of you components stateless.
State less component means it does not contains state, component only updates through props. So you can use class container for that. here is the solution...
import React, { Component } from "react";
class Login extends Component {
constructor(props) {
super(props);
this.emailInput = React.createRef();
this.passwordInput = React.createRef();
}
render() {
const { classes, width } = this.props;
// Flip container to column on mobile screens.
const panelDirection = width === "xs" ? "column" : "row";
return (
<Grid container direction="row" spacing={0} justify="center" alignItems="center" className={classes.background}>
<Grid item sm={10} xs={12} className={scss.panel}>
<form className="full-height" action="post">
<Grid direction={panelDirection} container spacing={0}>
<Grid item sm={6} xs={12}>
<Card className={classNames(scss.card, classes["primary-card"])}>
<CardContent className={scss["signup-content"]}>
<img src={logoImage} className={scss["signup-logo"]} alt="logo" />
<Typography variant="headline" component="h2" gutterBottom>
Web Portal
</Typography>
</CardContent>
<CardActions>
<Button fullWidth href="/register" color="secondary" variant="raised">
Create an account
</Button>
</CardActions>
</Card>
</Grid>
<Grid item sm={6} xs={12}>
<Card className={scss.card}>
<CardContent>
<TextField ref={this.emailInput} label="Email Address" fullWidth />
<TextField ref={this.passwordInput} label="Password" fullWidth margin="normal" type="password" />
</CardContent>
<CardActions className={scss["login-actions"]}>
<Button href="/login" color="primary" variant="raised">
Login
</Button>
<Button href="/forgot-password">Forgot Password</Button>
</CardActions>
</Card>
</Grid>
</Grid>
</form>
</Grid>
</Grid>
)
}
}
export default Login;
Now you can get value of the textfields like this
this.emailInput.current.value and this.passwordInput.current.value

Resources