React-grid-layout: Layout is not respected when passed by props

Created on 26 Oct 2018  路  12Comments  路  Source: STRML/react-grid-layout

Hello !
I am encountering a problem with RGL and I don't know if it's a bug or me doing things badly.
Demo here

What I'm trying to do

I'd like to create a dashboard using react-grid-layout. I'm trying to implement the container pattern so I have a DashboardContainer component controlling the layout _via_ his state and passing this layout down to a Dashboard component in the props.

The problem I encounter

Elements are placed correctly for the initial state but when I add items, the x, y, w and h of the added element aren't correct.
It should be (5,0,2,2) for the first addition, and it ends up being placed on (0,4,1,1)
I saw that elements can be redimensionned to 1 of width and height if they don't have the correct key, but I think my keys are correct.

Example: this layout is passed to Dasboard

image

But this is the displayed layout (the element "new0" isn't corresponding)

image

My code

Dashboard Container :

import React from "react";
import ReactDOM from "react-dom";
import Dashboard from "./Dashboard";

class DashboardContainer extends React.Component {
  static defaultProps = {
    maxRows: 8,
    cols: 12
  };

  state = {
    layout: [
      { i: "key0", x: 0, y: 0, w: 2, h: 2 },
      { i: "key1", x: 2, y: 0, w: 3, h: 2 },
      { i: "key2", x: 2, y: 2, w: 2, h: 2 },
      { i: "key3", x: 8, y: 0, w: 3, h: 2 }
    ],
    newElementsCount: 0
  };

  onLayoutChange = l => {
    this.setState({ layout: l });
  };

  add = () => {
    // all this is just to calculate where the new element should be placed
    var x = 0,
      y = 0,
      w = 2,
      h = 2,
      arr = [],
      trouve = false;
    for (let i = 0; i < 12; i++) {
      arr.push([]);
      for (let j = 0; j < 8; j++) {
        arr[i].push(1);
      }
    }
    this.state.layout.forEach(function(e) {
      for (let i = 0; i < e.w; i++) {
        for (let j = 0; j < e.h; j++) {
          arr[e.x + i][e.y + j] = 0;
        }
      }
    });
    var checkOk = (arr, w, h, x, y) => {
      var res = true;
      for (let i = x; i < x + w; i++) {
        for (let j = y; j < y + h; j++) {
          if (!arr[i][j]) res = false;
        }
      }
      return res;
    };
    if (checkOk(arr, w, h, x, y)) trouve = true;

    while (!trouve && y + h <= 8) {
      if (x + w === 12) {
        x = 0;
        y++;
      } else {
        x++;
      }
      if (checkOk(arr, w, h, x, y)) trouve = true;
    }
    if (trouve) {
      var newLayout = this.state.layout;

      newLayout.push({
        i: "new" + this.state.newElementsCount,
        x: x,
        y: y,
        w: w,
        h: h
      });

      this.setState({
        newElementsCount: this.state.newElementsCount + 1,
        layout: newLayout
      });
    } else {
      console.log("Can't add");
    }
  };

  render() {
    return (
      <div>
        <Dashboard
          layout={this.state.layout}
          add={this.add}
          cols={this.props.cols}
          maxRows={this.props.maxRows}
          onLayoutChange={this.onLayoutChange}
          preventCollision={true}
        />
      </div>
    );
  }
}

const contentDiv = document.getElementById("root");
ReactDOM.render(React.createElement(DashboardContainer), contentDiv);

Dashboard :

import React from "react";
import RGL, { WidthProvider } from "react-grid-layout";
const ReactGridLayout = WidthProvider(RGL);

class Dashboard extends React.Component {
  generateDOM() {
    return this.props.layout.map(function(item) {
      return <div key={item.i}>{item.i}</div>;
    });
  }

  render() {
    return (
      <div>
        <button onClick={this.props.add}>Add</button>
        <ReactGridLayout
          rowHeight={30}
          maxRows={this.props.maxRows}
          cols={this.props.cols}
          layout={this.props.layout}
          compactType={null}
          onLayoutChange={this.props.onLayoutChange}
        >
          {this.generateDOM()}
        </ReactGridLayout>
      </div>
    );
  }
}

module.exports = Dashboard;

You can find a codesandbox with this exact code here

I'm still very new to React and RGL so any suggestions are welcome !

Thank you

stale

Most helpful comment

@CWSites did you solve the data-grid being ignored issue?

All 12 comments

Yes @tlegluhe, I think there are some bugs when using this library in controlled mode...

Any update on that?

I ended up using the second way of passing data to react-grid-layout, with data-grid.
My generateDOM method in Dashboard now looks like

return this.props.layout.map(function(e) {
      return (
        <div
          key={e.i}
          data-grid={{
            x: e.x,
            y: e.y,
            w: e.w,
            h: e.h
          }}
        >
          {e.i}
        </div>
      );
    });

And I no longer pass this.props.layout to ReactGridLayout

Changed codepen here

Thank you @tlegluhe!

I'm also running into this issue, it's as if data-grid is completely ignored and doesn't override anything.

@STRML, I think this can be closed

@CWSites did you solve the data-grid being ignored issue?

@CWSites On the contrary, I finally used data-grid and it worked better than layout. Maybe we can consider this a workaround but it feels strange not to be able to control RGL by the layout property.

@zhenximi, no but I created a separate issue for that #900

@tlegluhe, I think the reason you're having trouble is due to the complexity of your add(). I recommend following the working sample here: https://strml.github.io/react-grid-layout/examples/6-dynamic-add-remove.html for how to add/remove items. Especially look at how onAddItem() works.

One thing to note is that when you are creating a new item it's important to get your x right. The math in the example above should work. I noticed that your items are all different sizes so your math to figure out x will be a little more complex. If all of your items were the same width then this would work... (currentLayoutLength * widthOfNewItem) % (ResponsiveColumnSize)

I did something very similar and this is the rough premise. It relies on passing the ability to set the parent's state layout into the addItem(). If all is done correctly then when you add a new widget it will save the updated layout back to the parent state and your grid will update.

<AddChartComponent setParentState={this.setState.bind(this)} currentLayout={this.state.layout} />

addItem() {
    let newLayout = this.props.currentLayout;

    // figure out the math and create a new Item, I'm using 'x: 4' as an example.
    const newItem = { i: 'uniqueID', x: 4, y: Infinity, w: 2, h: 2 };

    // add the new item to the layout
    newLayout.push(newItem);

    // save the updated layout to parent state
    this.props.setParentState({ layout: newLayout });
}

@CWSites I need my newly created element to be placed on the most top left spot in a layout composed of elements of different width, height and positions. There can be holes in this layout that should be filled when my new elements size allows it. Even though a simpler addItem than mine is surely possible (I really did it to work quickly), I can't make it as simple as you suggest.

To check if the problem is linked to the complexity of this method, I oversimplified it here.

On the first click on add, the new element should be placed between 1 and 3, but it does not, so I keep thinking that the layout is not correctly respected.

Thanks for the suggestions !

@tlegluhe I kinda had the same issue. I think the issue is when using the data-grid for sending the units. I switched to pass the layout as props and it works fine.

This is what I 'm using and it works fine:

addItem = i => {
const newLayouts = Object.assign({}, this.state.layouts);
Object.keys(defaultLayout).forEach(size => {
const widget = _.filter(defaultLayout[size], n => n.i === i); /* You can pass your units */
if (widget.length !== 0) {
newLayouts[size] = newLayouts[size].concat(widget);
}
});
this.setState(prevState => ({
layouts: newLayouts,
}));
};

@vaishkish can you cleanup the code in your post please?

@tlegluhe passing layout as props is definitely the way to go. for some reason data-grid is having issues and I haven't been able to pinpoint it...but that is part of what I brought up in #900

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this issue will be closed in 7 days

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sasha240100 picture sasha240100  路  3Comments

fsalamida picture fsalamida  路  3Comments

victor-unsw picture victor-unsw  路  3Comments

uditdubey picture uditdubey  路  3Comments

gerahimanshu picture gerahimanshu  路  4Comments