uneven spacing when using material ui grid - javascript

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

Related

Chart Data shows it's never updated through my setState variable

I have a popup dialog where I get a bunch of values from the user and then get a response after making an API request. I put an inline conditional rendering on the dialog box as it should only render once chart data is updated from the response. However, the dialog never appears even if console.log shows the data is updated. I tried to use useEffect() with many functions but it did not work. Any idea how to refresh the data again?
Edit: Added only relevant code
const [barGraphData, setBarGraphData] = useState([]);
const funcSetBarGraphData = (newBarGraphData) => {
setBarGraphData(newBarGraphData);
};
const sendChartData = async () => {
let bar_response = await axios.post(
"http://localhost:8080/h2h-backend/bardata",
bar_data,
{headers: {'Content-Type': 'application/json'}}
).then(res=>{
const resData = res.data;
const resSubstring = "[" + resData.substring(
resData.indexOf("[") + 1,
resData.indexOf("]")
) + "]";
const resJson = JSON.parse(resSubstring);
console.log(typeof resJson, resJson);
funcSetBarGraphData(barGraphData);
}).catch(err=>{
console.log(err);
});
chartClickOpen();
};
Returning popup dialog with charts when button is clicked:
<StyledBottomButton onClick={sendChartData}>Submit</StyledBottomButton>
{barGraphData.length > 0 && <Dialog
fullScreen
open={openChart}
onClose={chartClickClose}
TransitionComponent={Transition}
>
<AppBar sx={{ position: 'relative' }}>
<Toolbar>
<Typography sx={{ ml: 2, flex: 1 }} variant="h6" component="div">
Analytics View
</Typography>
<IconButton
edge="start"
color="inherit"
onClick={chartClickClose}
aria-label="close"
>
<CloseIcon />
</IconButton>
</Toolbar>
</AppBar>
<Grid container spacing={2}>
<Grid item xs={8} sx={{ pt: 2 }}>
<BarChart width={730} height={250} data={barGraphData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="business_name" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="num_of_customers" fill="#8884d8" />
<Bar dataKey="sum_total_amount" fill="#82ca9d" />
</BarChart>
{/* <Bar options={set_bar.bar_options} data={set_bar.bar_data} redraw={true}/> */}
</Grid>
<Grid item xs={4} sx={{ pt: 2 }}>
{/* <Pie data={data2} /> */}
</Grid>
</Grid>
</Dialog>}
<StyledBottomButton onClick={handleClose}>Cancel</StyledBottomButton>

If statement inside .map()?

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.

How can I store the information and render this hook in ReactJS?

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;

Map through an array in React

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;

Not able to see the cards

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>
))}

Categories