I am getting the data from backend & I am passing the data to Product.js. But Cards are not coming only search bar is coming. I am able to see the data in console using console.log(this.state.products);. Imports are there.
Here is my Products.js file content.
import Product from "./Product";
class Products extends Component {
constructor() {
super();
this.state = {
products: [],
searchString: ""
};
}
componentDidMount() {
axios.get("http://localhost:9022/products/getAll").then(res => {
this.setState({ products: res.data });
console.log(this.state.products);
});
}
render() {
return (
<div>
{this.state.products ? (
<div>
<TextField
style={{ padding: 24 }}
id="searchInput"
placeholder="Search for products"
margin="normal"
onChange={this.onSearchInputChange}
/>
<Grid container spacing={24} style={{ padding: 24 }}>
{this.state.products.map(currentProduct => (
<Grid item xs={12} sm={6} lg={4} xl={3}>
<Product products={currentProduct} />
</Grid>
))}
</Grid>
</div>
) : (
"No products found"
)}
</div>
);
}
}
export default Products;
Here is my Product.js file content.
const Product = props => {
return (
<div>
{props.product ? (
<Card>
<CardContent>
<Typography gutterBottom variant="headline" component="h2">
{props.product.fields.title}
</Typography>
<Typography component="p">{props.product.fields.id}</Typography>
</CardContent>
</Card>
) : null}
</div>
);
};
export default Product;
Its a typo. Its props.producs but not props.product. You are passing products as a prop to Product component but accessing as props.product so you need to access it using props.products. Try the below corrected code
const Product = (props) => {
return(
<div>
{ props.products ? (
<Card>
<CardContent>
<Typography gutterBottom variant="headline" component="h2">
{props.products.title}
</Typography>
<Typography component="p">
{props.products.id}
</Typography>
</CardContent>
</Card>
): null }
</div>
)
}
export default Product;
Also when you do .map or .forEach or for loop you should add unique id as key to the parent jsx element inside loop. iN your code you need to add unique like below
If you have unique id from each currentProduct then do this
{ this.state.products.map(currentProduct => (
<Grid key={currentProduct.id} item xs={12} sm={6} lg={4} xl={3}>
<Product products={currentProduct} />
</Grid>
))}
otherwise add index as key like below
{ this.state.products.map((currentProduct, index) => (
<Grid key={`Key_${index}`} item xs={12} sm={6} lg={4} xl={3}>
<Product products={currentProduct} />
</Grid>
))}
Related
In both the images the spacing on the right is more,I'm not using any custom css to alter the padding or margin
the Mui grid system seems to give me this result
spacing on the right
same on mobile screen
code for grid
const MovieCard = (props) => {
const lengthSettler = (text) => {
if (text.length > 301) {
return `${text.substring(0, 300)}...Read More`;
} else {
return text;
}
};
const print = () => {
console.log('MovieCard', props);
};
print();
return (
<Grid container direction={'row'} justify="center" spacing={6}>
{props.movies.map((movie) => (
<Grid item xs={4}>
<Card sx={{ maxWidth: 345, backgroundColor: '#ece3e3' }}>
<CardActionArea>
{movie.poster_path && (
<CardMedia
component="img"
image={`https://image.tmdb.org/t/p/w500/${movie.poster_path}`}
alt={movie.title}
/>
)}
{!movie.poster_path && (
<CardMedia
component="img"
image="https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/No-Image-Placeholder.svg/1665px-No-Image-Placeholder.svg.png"
alt={movie.title}
/>
)}
<CardContent>
<Typography gutterBottom variant="h5" component="div">
{movie.title}
</Typography>
{movie.overview && (
<Typography variant="body2" color="text.secondary">
{lengthSettler(movie.overview)}
</Typography>
)}
</CardContent>
</CardActionArea>
</Card>
</Grid>
))}
</Grid>
);
};
export default MovieCard;
things I tried
adding justify space-between
removing all the grid items
reinstalling mui
I have an array of object which contains images:
import React, { Fragment } from "react"
import { useStaticQuery, graphql } from "gatsby"
import Button from "#material-ui/core/Button"
import Card from "#material-ui/core/Card"
import CardActions from "#material-ui/core/CardActions"
import CardContent from "#material-ui/core/CardContent"
import CardMedia from "#material-ui/core/CardMedia"
import { CardActionArea, GridList } from "#material-ui/core"
import CssBaseline from "#material-ui/core/CssBaseline"
import Grid from "#material-ui/core/Grid"
import Toolbar from "#material-ui/core/Toolbar"
import Typography from "#material-ui/core/Typography"
import Container from "#material-ui/core/Container"
import Link from "#material-ui/core/Link"
import homePageStyles from "../ui/styles/homePageStyles"
import mobile from "../../images/mobileApps.jpeg"
import coporation from "../../images/coporations.png"
import statute from "../../images/gavel.png"
import policy from "../../images/policies.png"
export default function HeroBlock() {
const classes = homePageStyles()
const data = useStaticQuery(graphql`
query getCardCategoriesAndCounts {
allStrapiCategory {
edges {
node {
category
strapiId
}
}
}
allStrapiMobileApplication {
totalCount
}
allStrapiCorporation {
totalCount
}
allStrapiStatutoryLaw {
totalCount
}
allStrapiPolicyIssue {
totalCount
}
}
`)
const categories = data.allStrapiCategory.edges
const categoryImages = [
{ cImg: mobile },
{ cImg: coporation },
{ cImg: statute },
{ cImg: policy },
]
console.log(categoryImages)
const catImage = categoryImages.map(categoryImage => {
return (
<Fragment>
<CardMedia
image={categoryImage.cImg}
className={classes.images}
></CardMedia>
</Fragment>
)
})
const cards = (
<Fragment>
{categories.map(c => (
<Card style={{ width: "15.625rem", wordWrap: "break-word" }}>
<div>{catImage}</div>
<CardActionArea>
<CardContent>
<Typography variant={"h6"}>
{c.node.category.toUpperCase().replace(/_/g, " ")}
</Typography>
<Fragment>
{c.node.category === "Mobile_Apps" ? (
<Typography variant="paragraph">
{`There are currently ${data.allStrapiMobileApplication.totalCount} mobile applications.`}
</Typography>
) : c.node.category === "Corporations" ? (
<Typography variant="paragraph">
{`There are currently ${data.allStrapiCorporation.totalCount} coporations.`}
</Typography>
) : c.node.category === "Statutes" ? (
<Typography variant="paragraph">
{`There are currently ${data.allStrapiStatutoryLaw.totalCount} statutory laws.`}
</Typography>
) : c.node.category === "Policy_Issues" ? (
<Typography variant="paragraph">
{`There are currently ${data.allStrapiPolicyIssue.totalCount} policy issues.`}
</Typography>
) : (
<div></div>
)}
</Fragment>
</CardContent>
</CardActionArea>
</Card>
))}
</Fragment>
)
return (
<React.Fragment>
<CssBaseline />
<main>
<div className={classes.heroContent}>
<Grid container>
<Grid item classes={{ root: classes.heroColumn }}>
<Grid container>
<Grid
item
container
direction="column"
classes={{ root: classes.hero }}
>
<Typography component="h1" variant="h2" align="center">
MIKE <br /> The Mobile Information Knowledge Ecosystem
</Typography>
<Typography variant="h5" align="center" paragraph>
<strong>
<em>The</em>{" "}
</strong>
central repository for how to extract data from a mobile
device, extract data from a mobile application, what data
the mobile app provider retains, and the specific laws
pertaining to accessing that data.
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
</div>
<Grid Container>
<Grid item container justifyContent="space-evenly">
{cards}
</Grid>
</Grid>
</main>
</React.Fragment>
)
}
Updated with the entire code base. There is multiple map statements. If that is the issue how do you map multiple arrays inside React? Does that require using some sort of data store? It is returning each one of the images 4 times, rather than each images per card.
You generate an array of card media components, and then render this within another loop, so this is why you see the 4 category images repeated for each category mapped later.
const catImage = categoryImages.map(categoryImage => { // array
return (
<Fragment>
<CardMedia
image={categoryImage.cImg}
className={classes.images}
></CardMedia>
</Fragment>
)
})
const cards = (
<Fragment>
{categories.map(c => (
<Card style={{ width: "15.625rem", wordWrap: "break-word" }}>
<div>{catImage}</div> // <-- render array of images here!!
<CardActionArea>
...
Assuming the categoryImages and categories arrays have the same length and sorted order, then you can render the card media component directly in the mapping and use the current index to access the correct image from the categoryImages array.
const cards = (
<Fragment>
{categories.map((c, index) => ( // <-- use current index
<Card style={{ width: "15.625rem", wordWrap: "break-word" }}>
<CardMedia
image={categoryImages[index].cImg} // <-- access by index
className={classes.images}
/>
<CardActionArea>
...
Array.map returns array. So catImage is an array that contains components.You can use each CardMedia component by indicating index number.
catImage[index]
catImage[0] is the first card.
So you can update your code like below:
const cards = ( // <==== line 74
<Fragment>
{categories.map((c, index) => (
<Card style={{ width: "15.625rem", wordWrap: "break-word" }}>
<div>{catImage[index]}</div>
I hope my answer will be solve your problem.
Thanks.
I'd like to know how I can implement an if statement inside .map()
See code below.
Currently the delete button is disabled if the image is not uploaded by the current user, but my goal is to not render the delete button at all.
return <Grid container justify="center" spacing={2}>
{/* All images */}
{docs && docs
// For every image
.map(image => (
// In a grid item
<Grid className="img-item" item key={image.id} xs={12} md={6} lg={4}>
{/* all accounts */}
{docs2 && docs2
// For every single image:
// Filter statament only contains the user of specific image
// https://medium.com/poka-techblog/simplify-your-javascript-use-map-reduce-and-filter-bd02c593cc2d
.filter((user) => image.userID === user.userID)
//Now you have the user that belongs to the image.ID
.map(user => (
<div key={image.id}>
<img src={image.url} alt="uploaded pic" />
<Typography variant="subtitle1"> By {user.userName}
{/* How do I implement this if statement for the IconButton? */}
{/* if ({handleButton(image.userID)} === false){
return
} */}
{/* Delete button */}
<IconButton
disabled={handleButton(image.userID)}
color="secondary" aria-label="delete image"
onClick={() => handleDeleteImage(image.id, image.userID, image.name)}
component="span" >
<DeleteForever />
</IconButton>
</Typography>
</div>
))}
</Grid>
))}
</Grid>
}
export default ImageGrid;
What you are looking for is
return <Grid container justify="center" spacing={2}>
{/* All images */}
{docs && docs
// For every image
.map(image => (
// In a grid item
<Grid className="img-item" item key={image.id} xs={12} md={6} lg={4}>
{/* all accounts */}
{docs2 && docs2
// For every single image:
// Filter statament only contains the user of specific image
// https://medium.com/poka-techblog/simplify-your-javascript-use-map-reduce-and-filter-bd02c593cc2d
.filter((user) => image.userID === user.userID)
//Now you have the user that belongs to the image.ID
.map(user => (
<div key={image.id}>
<img src={image.url} alt="uploaded pic" />
<Typography variant="subtitle1"> By {user.userName}
{/* How do I implement this if statement for the IconButton? */}
{/* if ({handleButton(image.userID)} === false){
return
} */}
{/* Delete button */}
{ handleButton(image.userID) &&
<IconButton
disabled={handleButton(image.userID)}
color="secondary" aria-label="delete image"
onClick={() => handleDeleteImage(image.id, image.userID, image.name)}
component="span" >
<DeleteForever />
</IconButton>
}
</Typography>
</div>
))}
</Grid>
))}
</Grid>
}
export default ImageGrid;
you just need to add curly bracelet and return;
const items = [1,2,3,4,5,6].map(item => {
if(item > 2){
return item;
}
return -1;
});
console.log(items);
You can return null as follows:
const imgArray = ['img1', 'img2', 'img3', 'img4', 'img5'];
imgArray.map((item) => {
if (item === 'img4') {
return null;
}
return null;
});
return imgArray;
ReactJs will not render null.
I use a button from another component to access this Favorites component, but when I press the button, it doesn't load anything and the page goes blank. Now if I press the button twice, it shows me the information.
const Favorites = (props) => {
const [products, setProducts] = useState([]);
useEffect(() =>{
let productsArray = []
firebase.database().ref().child('favorites').orderByKey()
.once('value', snap => {
snap.forEach(child => {
if(child.val().user_id === firebase.auth().currentUser.uid){
firebase.database().ref('products/' + child.val().product_id)
.once('value')
.then(snapshot =>{
const favorite = {
id: snapshot.key,
name: snapshot.val().name,
category: snapshot.val().category,
description: snapshot.val().description,
image: snapshot.val().image,
price: snapshot.val().price,
stock: snapshot.val().stock,
};
productsArray.push(favorite);
});
}
});
setProducts(productsArray);
});
},[]);
return (
<Fragment>
<ul>
{ products && products.map((item, index) => {
return(
<Grid container justify="center" alignItems="center" key={index}>
<div className={classes.root}>
<Paper className={classes.paper}>
<Grid container spacing={2}>
<Grid item>
<ButtonBase className={classes.image}>
<img className={classes.img} alt="complex" src={item.image} />
</ButtonBase>
</Grid>
<Grid item xs={12} sm container>
<Grid item xs container direction="column" spacing={2}>
<Grid item xs>
<Typography gutterBottom variant="subtitle1">
{item.name}
</Typography>
<Typography variant="body2" gutterBottom>
{"Categoria: " + item.category}
</Typography>
<Typography variant="body2" color="textSecondary">
{item.description}
</Typography>
</Grid>
<Grid item>
<Button
onClick={(event) => removeFavorite(event, index)}>
<HighlightOff/> Eliminar de Favoritos
</Button>
</Grid>
</Grid>
<Grid item>
<Typography variant="subtitle1">{"Bs " + item.price + "/ Kg"}</Typography>
</Grid>
</Grid>
</Grid>
</Paper>
</div>
</Grid>
);
})
}
</ul>
</Fragment>
);
}
export default Favorites;
I think I am doing something wrong with the useEffect() when I perform the query and store it with the setProducts() fix. I don't know what's happening, why does it go blank and then rerun the Favorites component if it shows? I appreciate your help.
In the App component I have the Shopping Cart Sharp button, which redirects to "/ shoppingcart", which is the component where I have the problem.
const MyLink = React.forwardRef((props, ref) => <RouterLink innerRef={ref}
{...props} />);
function App() {
const [user, setUser] = useState(null);
const onLogout = () => {
setUser(null);
};
return (
<Router>
<CssBaseLine/>
<Header user={user}>
{user && <Button to="/shoppingcart" component={MyLink}
color="inherit"><ShoppingCartSharp /></Button>}
</Header>
<Routes/>
</Router>
);
}
export default App;
I'm going to try and explain this as best I can, bear with me please sorry. I have an array called works that contains multiple objects from a portfolio. I imported the array into my component file and I can just rewrite it over and over but I have multiple elements for each value and my code will be very long. I feel like that isn't very DRY. How I can just put the information into my component once and have it iterate through everything in the array.
Here is a prototype of how I am currently doing it.
class PortfolioCard extends React.Component {
render() {
return (
<Card className>
<CardHeader
avatar={<Avatar aria-label="Recipe">R</Avatar>}
title={works[0].title}
subheader={works[0].name}
/>
<CardMedia className image={works[0].pic} />
<CardContent>
<Typography component="p">
{works[0].desciption}
</Typography>
</CardContent>
<CardActions className disableActionSpacing>
<IconButton aria-label="Live Site">
<FavoriteIcon> {works[0].link}
</FavoriteIcon>
</IconButton>
<IconButton aria-label="Github">
<ShareIcon> {works[0].github}
</ShareIcon>
</IconButton>
</CardActions>
</Card>
);
}
}
You can use .map to render list of cards something like below in render. Also when you render array of jsx elements don’t forget to set unique key to top jsx element in your case it’s Card
return (
{works.map((work, index) => (<Card key={"Key-"+index} className>
<CardHeader
avatar={<Avatar aria-label="Recipe">R</Avatar>}
title={work.title}
subheader={works.name}
/>
<CardMedia className image={work.pic} />
<CardContent>
<Typography component="p">
{work.desciption}
</Typography>
</CardContent>
<CardActions className disableActionSpacing>
<IconButton aria-label="Live Site">
<FavoriteIcon> {work.link}
</FavoriteIcon>
</IconButton>
<IconButton aria-label="Github">
<ShareIcon> {work.github}
</ShareIcon>
</IconButton>
</CardActions>
</Card>))}
);
You can use array#map to render your array with all the information.
class PortfolioCard extends React.Component {
render() {
return (
<Card className>
{works.map(({title, name, desciption, pic, link, github}) => ({
<React.Fragment>
<CardHeader
avatar={<Avatar aria-label="Recipe">R</Avatar>}
title={title}
subheader={name}
/>
<CardMedia className image={pic} />
<CardContent>
<Typography component="p">
{desciption}
</Typography>
</CardContent>
<CardActions className disableActionSpacing>
<IconButton aria-label="Live Site">
<FavoriteIcon> {link}
</FavoriteIcon>
</IconButton>
<IconButton aria-label="Github">
<ShareIcon> {github}
</ShareIcon>
</IconButton>
</CardActions>
<React.Fragment>
}))}
</Card>
);
}
}
Does this example help?
import React, { Component } from 'react';
class App extends Component {
state = {
works: [
{ name: 'First Work' },
{ name: 'Second Work' },
{ name: 'Third Work' },
],
};
render() {
return (
<div>
{this.state.works.map((work, i) => {
return <div key={i}>{work.name}</div>;
})}
</div>
);
}
}
export default App;