How to avoid initial lag after input using Material UI - reactjs

Context
I have a carousel of MUI cards for a website that I'm building that is a box using a stack as it's underlying component. A problem that's come up is that whenever I try to scroll, there's at least a 4 second lag before seeing any new render. I tried
cropping down the images
compressing them
converting to .webp's
moving the logic into one place instead of passing props
Thouhgts
Memoizing the component works as a band-aid after the initial lag and
it scrolls as it should after but I'm assuming that's not the correct way
to do this.
(Idea) After looking back into the docs for an alternative I figured
virtualizing the list might work as well
Would probably be easier to just write them out by hand
Cut off a lot of the unnecessary bits
Index.tsx snippet mapping through data
<Box
component={Stack}
direction="row"
spacing={5}
>
{carouselData.map((item: CarouselProps) => (
<CreativeCarousel
src={item.src}
/>
))}
</Box>
Carousel Component
//Consistent typing for properties
export type CarouselProps = {
src: StaticImageData;
};
const CreativeCarousel = (props: CarouselProps) => {
return (
<Card sx={{ maxWidth: 300, minWidth: 300, height: "100%" }}>
<CardMedia component="img" height="75" image={props.src.src} />
</Card>
);
};
export default CreativeCarousel;

Troubleshooting
The lag went away after removing the <CardMedia /> so my guess is that rerendering full-res images for every frame of scrolling isn't the most optimal move.
Solution
But replacing the underlying component from the standard html img to an optimized Next.js Image most definitely was. I was under the impression that I needed to pass a component as a property or else I'd need to use an img like what was used in the example. I only found out later that I could also pass react nodes as children in the API page.
const Carousel = () => {
return carouselData.map((item) => (
<Card
key={item.heading}
sx={{ maxWidth: 300, minWidth: 300, height: "100%", mx: 4 }}
>
<CardMedia sx={{ width: "100%", height: "auto" }}>
<Image
alt={item.heading}
src={item.src}
layout="responsive"
/>
</CardMedia>
</Card>
));
};
export default Carousel;

Related

MUI alternative to CardHeader

Good evening,
I am looking for a better way to display what you see in the picture. With the CardHeader this is all not centered and I can't use any useState methods.
how it should look
<Card sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
<CardHeader avatar={<Avatar sx={{ bgcolor: red[500] }} aria-label="logo">N</Avatar>} action={<IconButton aria-label="enable"><FavoriteBorderIcon /></IconButton>} title="Netflix" subheader="netflix.com" />
</Card>
The way it looks here is how I want it to look in the end, but I can't useState methods here. Also the content (e.g. the icon) is not centered vertically.
I dont know why there is a problem with useState hook. Feel free to add it to the and depending on the state switch between normal and red icon.
https://codesandbox.io/s/distracted-oskar-1h7xmj?file=/src/App.js

Half page image on MUI v5

I'm trying to create a landing page in MUI v5 where half of the page is an image and the second part of the page is a form to login. I want the image and form to always fill the page completely with no scroll.
However, when doing so, it seems that MUI root is always affecting the margin so that the view has a horizontal scroll and there is white space on the left-hand side (See example below - and ignore the silly image):
How the page should look (and what it looks like if I scroll to the right):
How the page should look when I load it before scrolling:
I've spent hours trying to edit the CSS and figuring out where this is coming from with no luck. There is probably a better way to format this but I am using Grid and have tried Box with no luck.
My source code looks something like:
import React from "react";
import PropTypes from "prop-types";
import Grid from "#mui/material/Grid/Grid";
import Box from "#mui/material/Box";
import Paper from "#mui/material/Paper";
import SomeComponent from "...{some_path}...";
import SomeFormComponent from "...{some_path}...";
const SomeComponent = ({children, title, width}) => {
return (
<Grid container disableGutters sx={{height: "100vh", width: "100vw"}}>
<Grid
item
xs={false}
sm={4}
md={7}
sx={{
backgroundImage: `url(${SomeImage})`,
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
backgroundPosition: "center",
}}
/>
<Grid item xs={12} sm={8} md={5} component={Paper} elevation={6} square>
<Box
sx={{
my: 8,
mx: 4,
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<SomeFormComponent width={width} title={title}>
{children}
</SomeFormComponent>
</Box>
</Grid>
</Grid>
);
};
Also, inspecting the page elements, I've identified the padding and margin come from a class called css-ayh9c9-MuiGrid-root which seems to be causing this because if I remove the class from the parent, everything works as expected. However, this is being added by MUI behind the scenes because it is nowhere in my source code.
Any help would be greatly appreciated!
I prefer Box whenever possible because Grid does some funky stuff with margins. In this case you can use the flex attribute on the children to get the right proportions. here is a working example https://codesandbox.io/s/material-demo-forked-rur3t?file=/App.tsx

scroll to first element using List and AutoSizer doesn't work

I wanted to scroll back up to the first element in the List when a user clicks pagination buttons.
So far I come across scrollToRow and scrollToIndex and both of them didn't work.
Here's my current code:
<AutoSizer disableWidth>
{({ height }) => (
<div>
<List
ref="list"
height={height}
rowCount={this.state.items.length}
rowHeight={115}
rowRenderer={this._rowRenderer}
width={1}
scrollToRow={0}
containerStyle={{
width: '100%',
maxWidth: '100%',
}}
style={{
width: '100%',
marginBottom: '10px',
}}
/>
</div>
)}
</AutoSizer>
After a little bit of thinking, I found out that there's no need to use react-virtualized package anymore. Since I refactored the list to make use of SSR pagination showing 24 items at a time. So, it was an overkill.
Anyway, I just reused the same _rowRenderer() function to map items into a list. To achieve the scrolling behavior I just added the styling prop of "overflow: scroll".
Thats all.

Why is there massive white space under my image in React? What's creating this mysterious div?

I'm building a login Component in React, and trying to add the brand image. When I do this, I'm getting a huge amount of white space underneath it.
I've been working on this for a bit now, and I've discovered that, when I look in the dev tools, I can make headway by modifying this mysterious div that seems to be created by Material-UI, the front-end framework I'm using.
When I look into this in the dev tools, I find that there's a div with the attribute padding-top: calc(100%) which, when modified to something like padding-top: calc(30%), reduces the size of this whitespace underneath the image.
I've also tried some of the basic layout solutions suggested in many of the answers to similar questions here on SO, but none of them make a difference. It seems that this padding issue is at the heart of the problem.
The Problem
Because I don't know what's creating this div, I'm not able to override the padding to work towards a solution. I've tried modifying paddingTop and padding with the !important tag in the styling of both the image, and the parent element of the image.
Code Sample
<Paper variant='outlined' style={{ width: '380px' }}>
<Box m={4}>
<Image
src='../static/images/TextLogo.svg'
imageStyle={{
height: 'auto',
width: '100%',
}}
/>
</Box>
...
Stack
"#material-ui/core": "^4.0.0-alpha.8",
"material-ui-image": "^3.2.3",
"next": "^8.1.0",
"react": "^16.8.6",
"react-dom": "^16.8.6"
Thanks. I'd appreciate your help!
The <div> with the padding-top and other styles is coming from the Image component that you are using from material-ui-image.
Below is the overall structure rendered by that Image component:
<div
style={styles.root}
onClick={onClick}
>
{image.src && <img
{...image}
style={styles.image}
onLoad={this.handleLoadImage}
onError={this.handleImageError}
/>}
<div style={styles.iconContainer}>
{!disableSpinner && !this.state.imageLoaded && !this.state.imageError && loading}
{!disableError && this.state.imageError && errorIcon}
</div>
</div>
padding-top is part of the styles in styles.root.
styles.root:
const styles = {
root: {
backgroundColor: color,
paddingTop: `calc(1 / ${aspectRatio} * 100%)`,
position: 'relative',
...style
},
When padding-top is a percentage, it is a percentage of the width, so it is important to control the width of the container in order to have predictable behavior.
You can modify the padding-top by either explicitly overriding it via the style prop or by specifying the appropriate value in the aspectRatio prop. By default, this Image component is assuming square images (aspectRatio: 1).
Here is a working example demonstrating both ways of controlling padding-top:
import React from "react";
import Image from "material-ui-image";
import Box from "#material-ui/core/Box";
export default function App() {
return (
<>
<Box m={4} width={200}>
<Image
aspectRatio={1.5}
src="https://www.publicdomainpictures.net/pictures/10000/velka/black-monkey-11280155875i3QV.jpg"
/>
Something under image 1
</Box>
<Box m={4} width={200}>
<Image
style={{
paddingTop: "calc(66.7%)"
}}
src="https://www.publicdomainpictures.net/pictures/10000/velka/black-monkey-11280155875i3QV.jpg"
/>
Something under image 2
</Box>
</>
);
}
Slightly related answer (with regard to the use of padding-top): A good way to handle #material-ui Skeleton scaling within a variable height grid row?

Image on Material UI CardMedia

I´m having some troubles on getting an image from props on a CardMedia image:
<Card className={classes.card}>
<CardMedia
className={classes.img}
image={this.props.recipe.thumbnail}
/>
<CardContent className={classes.content}>
<Typography gutterBottom variant="headline" component="h2">
{this.props.recipe.title}
</Typography>
<Typography component="p">
{this.props.recipe.ingredients}
</Typography>
</CardContent>
<CardActions>
<Button
href={this.props.recipe.href}
Recipe
</Button>
</CardActions>
</Card>
It just doesnt render at all the images; all the other props values are rendered as they has to.
Also as Material UI I had specified the CardMedia height on a JS css.
Does anyone knows why this happens?
I had the Same Issue. Finally Got it Working doing this:
const styles = {
media: {
height: 0,
paddingTop: '56.25%', // 16:9,
marginTop:'30'
}
};
<CardMedia
className={classes.media}
image={require('assets/img/bg2.jpg')} // require image
title="Contemplative Reptile"
style={styles.media} // specify styles
/>
You can also check :
https://codesandbox.io/s/9zqr09zqjo - No option to load images . The image location is my local
I have read the docs and also notice that you have to specify the height to display images. While they say you should create a component with style I feel that a simpler way of doing it is by using the style prop directly:
<CardMedia
style={{height: 0, paddingTop: '56.25%'}}
image={project.image}
title="lorem ipsum"
/>
The other options would be to create a style object first and then render the component withStyle as the docs said:
const styles = {
card: {
maxWidth: 345,
},
media: {
height: 0,
paddingTop: '56.25%', // 16:9
},
};
function SimpleMediaCard(props) {
const { classes } = props;
return (
<div>
<Card className={classes.card}>
<CardMedia
className={classes.media}
image="/static/images/cards/contemplative-reptile.jpg"
title="Contemplative Reptile"
/>
</Card>
</div>
);
}
export default withStyles(styles)(SimpleMediaCard);
to set fallback image on CardMedia, you can try this:
import FALLBACK_IMAGE from 'src/assets/images/fallback_image.png';
const onMediaFallback = event => event.target.src = FALLBACK_IMAGE;
<CardMedia
component="img"
className={classes.media}
image="/static/images/cards/contemplative-reptile.jpg"
title="Contemplative Reptile"
onError={onMediaFallback}
/>
I was facing the same issue.
A good workaround would be to use the 'img' element along with the 'src' attribute inside the CardMedia body instead of passing it as an attribute to the CardMedia.
<CardMedia>
<img src={this.props.recipe.thumbnail} alt="recipe thumbnail"/>
</CardMedia>
And while passing the props to the class I sent the image path as an object. In your case, suppose recipe is an object with thumbnail as one of the attributes. Then in the parent class, I would write the prop as:
...
recipe = {
.
.
thumbnail: require('assets/images/img1.jpg'),
//make sure the path of the image is relative to parent class where you are defining the prop
.
.
}
...
Here I am sending the path of the image as an object. This worked for me.
After updating from 4.9 => 4.12
add component="img" prop
(it had 0 height in DOM after the update despite setting it to 200px)
<CardMedia className={classes.pageMedia} component='img' image='/static/img.jpg' />
In my case it worked after changing the value of height other than 0 in the style
Not Worked:
const styles = {
card: {
maxWidth: 345,
},
media: {
height: 0,
paddingTop: '56.25%', // 16:9
},
};
Worked:
const styles = {
card: {
maxWidth: 345,
},
media: {
height: "300px" or "10em",
paddingTop: '56.25%', // 16:9
},
};
Try using src property instead of img property

Resources