react-grid-layout: Child component does not properly inherit GridItem props

Hi, I am rather new to React as a whole, so if this issue is off-base or needs clarification please don’t hesitate.

I am seeing an issue where I define a child component and try to instantiate it through the standard React constructor as seen below. “tile_info” is an object retrieved from a server which you can assume contains attributes seen in TilePanel

<ReactGridLayout
ref="rgl"
{...this.props}
layout={this.props.layout}>
    <TilePanel tile={this.props.tile_info} grid={this.generateGrid()}
</ReactGridLayout>
var TilePanel = React.createClass({
  render: function () {
    return (
        <div key={this.props.tile.title} class="widget-number" data-grid ={this.props.grid} >
          <h1 className="title">{this.props.tile.title}</h1>
          <h2 className="value">{this.props.tile.value}</h2>
          <p style={styles.last_updated}>{this.props.tile.last_update}</p>
        </div>
    )
  }
});

The resulting HTML for that GridItem does not contain the expected class/style attributes.
Expected:

<div class="react-grid-item widget-number     cssTransforms react-resizable" style="width: 270px; height: 310px; position: absolute; touch-action: none; transform: translate(10px, 10px);">

Actual:

<div class="widget-number" style="background-color:#ff9618;" data-reactid=".0.1.0.1.0:$/=1$daily_revenue">

Notice the lack of props on the class and style attributes.

If this isn’t clear, I will do my best to clarify

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 27 (7 by maintainers)

Most upvoted comments

Adding summary of what I’ve found, I feel like I have a good understanding of this issue

Explanation

Follow the code from ReactGridLayout to GridItem, then through Griditem.mixinDraggable to DraggableCore and Griditem.mixinResizable to Resizeable. This will show you exactly everything that needs to be passed down and also hopefully convince you to not bother. As others mentioned previously, you need to pass the className and style props. You also need to pass the children props if you’re using resize. You also need to pass 4 different event handlers for draggability. @DanielRoeven 's comment on passing {...this.props} seeks to blanket solve it, but this omits the necessity to pass down the children, which is why he doesn’t see the resize handle (a <span> child).

Solutions

Yes, you could get it working correctly with {…this.props} (or manually passing the ones you need, which I made work too) and passing the children down. But don’t. You’re exposing too many assumptions about how react-grid-layout, react-draggable, and react-resizable work - particularly parts of them that might forget to consider an “involved” use case like this if updated. I’d also be mindful wrapping your component in a div. You’re still probably going to need to do some things like add styles to the “real” div that RGL uses. I wanted to use my own component to keep my code more organized, and this approach (which I also tried) caused me to split out my styles, and created some temporary bugs out of confusion. You could also create a “faux” component - a vanilla function that returns a real component. This allowed me to keep all my “Widget” component info in one file and my “Dashboard” kinds of things in another file. eg.

const buildWidget = function(layoutRow) {
    return (
        <div className="widget" key={layoutRow.i}>
            <WidgetContent  />
        </div>
    )
};

However I think that design might have negative repercussions later that I haven’t seen yet.

Anyways, I’d agree there ought to be some authoritative info about this in the README or some updates to allow better support here, seems like this is a common use case.

I’ve spent 3 hours trying to figure out why my custom component doesn’t work until I’ve found this thread. It should be added to README.

@noizex - fwiw, I was able to work around this issue by wrapping my custom component with a div. Working code:

`

<div key={i} data-grid={{w: 2, h: 3, x: 0, y: 0}}> <CustomComponent title={l.header.title}/> </div>`

Yes - if you pass a custom component, be sure to take in a style and className prop, because RGL cannot force you to render it. You need to add className={'widget-number ' + this.props.className} style={this.props.style} to the main <div>.

If you need the resize handle as well, make sure to render the children in your TilePanel Component

I’m seeing the same issue as @noizex where resizing works, but dragging does not work with a custom component. Any ideas on this @STRML, @nitronick600, @hepiyellow, @larrydahooster ?

I encountered the same issue. I think it is worth adding to the README.

By the way , one possible solution is to make a ReactGridLayout wrapper which wraps it’s children with a <div> and puts the key property on that div. that way you don’t have to add anything to children you pass to the ReactGridLayoutWrapper.

It’s working I put {…this.props} on my custom components. READ.ME missed this

The trick is to set className to wrapper instead of widget-number

<div {...this.props} className={`widget-number ${this.props.className}`}>

<div {...this.props} className={`wrapper ${this.props.className}`}>

did you try:

var TilePanel = React.createClass({
  render: function () {
    return (
        <div key={this.props.tile.title} class="widget-number" data-grid ={this.props.grid} >
          {this.props.children}
          <h1 className="title">{this.props.tile.title}</h1>
          <h2 className="value">{this.props.tile.value}</h2>
          <p style={styles.last_updated}>{this.props.tile.last_update}</p>
        </div>
    )
  }
});