Material-ui: [GridList] Wrapping GridListTile in a component does not render properly

Created on 19 Jan 2018  路  5Comments  路  Source: mui-org/material-ui

  • [X] I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior

The GridListTile should look the same when wrapped in a component as when it's not wrapped.

Current Behavior

Wrapping GridListTile in a component collapses it, it does not fill out up the space it should.

Steps to Reproduce (for bugs)

Here is a demo: https://stackblitz.com/edit/react-ershgc
Output here: https://react-ershgc.stackblitz.io/

image

Context

1) I would like to be able to have onclick pass back the id of the clicked GridListTile without using a wrapper arrow function for the GridListTile onClick prop. Using a wrapper function created a new function on every re-render of the main component, which causes an update to each GridListTile.

2) Also having a wrapper component for GridListTile enables me to optimize renders with PureComponent/shouldComponentUpdate/onlyUpdateForKeys etc.

Your Environment

| Tech | Version |
|--------------|---------|
| Material-UI |1.0.0-beta.29|
| React |Latest|
| browser |Chrome, Mac, latest|

I am not sure if StackBlitz deletes demo apps after some period of time so pasting the code here too, just in case:

/*
  Experiment with moving GridListTile to it's own component.

  The reason for wanting this is to avoid using anonymous functions in the 
  GridListTile props. Generating functions makes every GridListTile update 
  on every update to 'list', shouldComponent update or PureComponent will 
  also re-render since the passed onClick function technically differs from 
  the previous one.
*/

import React, {Component} from 'react';
import {render} from 'react-dom';
import GridList, {GridListTile} from 'material-ui/GridList';

const list = ['id1', 'id2', 'id3'];

class App extends Component {
  handleClick = (event, id) => {
    console.log('id', id);
  };

  render() {
    return (
      <GridList cellHeight={192} cols={3}>
        {list.map(id => (
          <GridListTile
            // An anonymous event handler is needed to be able to pass
            // 'id' to the actual event handler
            onClick={event => this.handleClick(event, id)}
            key={id}
            cols={1}
            style={{background: 'red'}}
          >
            {id}
          </GridListTile>
        ))}

        {/*
          Wrapping GridListTile in a separate component solves the event
          handler issue, but this does also not render as expected
        */}
        {list.map(id => <MyTile key={id} id={id} onClick={this.handleClick} />)}
      </GridList>
    );
  }
}

class MyTile extends Component {
  handleClick = event => {
    // Passing the id without using an anonymous function in the JSX props
    this.props.onClick(event, this.props.id);
  };

  render() {
    return (
      <GridListTile cols={1} style={{background: 'green'}} onClick={this.handleClick}>
        {this.props.id}
      </GridListTile>
    );
  }
}

render(<App />, document.getElementById('root'));
ImageList question

Most helpful comment

After looking into this more, there is a style prop being injected to the props of the list.map() components. Applying it in the wrapper component solves the issue:

In the example above, change from:

  render() {
    return (
      <GridListTile cols={1} style={{background: 'green'}} onClick={this.handleClick}>
        {this.props.id}
      </GridListTile>
    );
  }

To:

  render() {
    return (
      <GridListTile cols={1} style={{...this.props.style, background: 'green'}} onClick={this.handleClick}>
        {this.props.id}
      </GridListTile>
    );
  }

And it works!

All 5 comments

After looking into this more, there is a style prop being injected to the props of the list.map() components. Applying it in the wrapper component solves the issue:

In the example above, change from:

  render() {
    return (
      <GridListTile cols={1} style={{background: 'green'}} onClick={this.handleClick}>
        {this.props.id}
      </GridListTile>
    );
  }

To:

  render() {
    return (
      <GridListTile cols={1} style={{...this.props.style, background: 'green'}} onClick={this.handleClick}>
        {this.props.id}
      </GridListTile>
    );
  }

And it works!

Keeping this issue open as I still believe it is an error.

After looking into this more, there is a style prop being injected to the props

@lirbank I'm glad you figured it out on your own. The composition requirements are exposed in https://material-ui-next.com/guides/composition/. I'm not sure what you mean by error. It's the expected behavior. I can't think of any better solution without introducing an intermediary DOM element (no go). Let us know if you find a better alternative, a pull-request is welcomed.

Oh, I see! It is passing a style prop instead of className/classes so I thought it was an artifact from the 0.1 version of material-ui.

I totally missed that page (about composition), thanks for letting me know.

How to use composition in this case then?

Was this page helpful?
0 / 5 - 0 ratings