reactjsmaterial-uivirtual-dom

React: Material-UI GridListTile looses style attributes when called from within component


I have a UI where I render a <GridList> with tiles that I define as custom components.

When I first wrote this, I was a bit confused about the nature of React hooks, how props are passed etc. So, I was calling it as a regular JS function:

const VideoTile = ({...props}) => {
    const { url, classes } = props;
    
    return (
        <GridListTile key={url} cols={1} className={classes.videoTile} >
            <video src={url} className={classes.videoPreview} />
            <GridListTileBar title={url} />
        </GridListTile>
    )
}
const VideoBrowser = ({...props}) => {
    const {vidUrls, classes} = props; //for the sake of brevity
    return (
        <GridList cellHeight='auto' cols={4} className={classes.videoGridList}>
          {videoUrls.map((url) => VideoTile({url, classes}))}
        </GridList>
    )
}

Notice that I was adding a key attribute to the tile within the function to appease React.

Reviewing the code with a better understanding of React, I changed it such that it can be called with JSX syntax, using hooks within the component etc and hopefully following better practice. However, I've discovered that something about the way Material-UI operates on React's virtual DOM means that it renders the two children here very differently:

(I've removed hooks for now and am focussing on rendering single components alongside each other so I can compare in devtools).

<GridList cellHeight='auto' cols={4} className={classes.videoGridList}>
  {VideoTile({url, classes})}
  <VideoTile url={url} classes={classes} />
</GridList>

The first version has some element style attributes { width: 25%; height: auto; padding: 2px; } added to the outer <li>. The latter version doesn't, presumably because the code that adds them doesn't understand that the top-level element of <VideoTile> is actually a <GridListTile>, and, as such, of interest. I suppose I'll just re-arrange the code so that the <GridListTile> appears outside the function, but I'd like to understand the problem better, how I might solve it differently, or whether there's a general principle that I should understand to avoid similar problems in future.


Solution

  • By the time I finished entering the question, I'd realised how to fix my particular bug for now, allowing more use of hooks within my components etc:

    const VideoTileInner = ({...props}) => {
        const { url } = props;
        const classes = useStyles();
        
        return (
            <>
                <video src={url} className={classes.videoPreview} />
                <GridListTileBar title={url} />
            </>
        )
    }
    
    const VideoBrowser = ({...props}) => {
        const {vidUrls} = props;
        const classes = useStyles();
        return (
            <GridList cellHeight='auto' cols={4} className={classes.videoGridList}>
            {videoUrls.map((url) => {
              return (
                <GridListTile key={url} cols={1} className={classes.videoTile}>
                  <VideoTileInner url={url} />
                </GridListTile>
              )
            })}
            </GridList>
        )
    }
    
    

    As mentioned in the question, though, I'd like to understand the problem better, how I might solve it differently, or whether there's a general principle that I should understand to avoid similar problems in future.

    Or, indeed, whether this could be considered a bug in Material-UI.