8

I am using react-grid-layout to make drag-drop and resize components, So i am able to achieve the functionality as mentioned in the doc.

My Issue

  • I am creating dynamic UI so I am rendering my UI with data.
  • Here in this library I need to create layout like { {i: 'a', x: 0, y: 0, w: 1, h: 2}
  • When I am creating static UI I am able to achieve what I want.Here is working code sandbox

In static Ui with predefined layout I did like below.

    const layout = [
    { i: "a", x: 0, y: 0, w: 2, h: 1 },
    { i: "b", x: 2, y: 0, w: 2, h: 1 },
    { i: "c", x: 4, y: 0, w: 2, h: 1 },
    { i: "d", x: 6, y: 0, w: 2, h: 1 },
    { i: "e", x: 0, y: 2, w: 2, h: 1 }
  ];

<div className="App">
  <GridLayout
    className="layout"
    layout={layout}
    style={{ backgroundColor: "blue" }}
  >
    {layout.map((li) => (
      <div key={li.i} style={{ backgroundColor: "yellow" }}>
        {li.i}
      </div>
    ))}
  </GridLayout>

This is working fine, But here the layout I have defined is static, So for this I searched and got one example with dynamic height and width, which is creating a dynamic layout.

But that is creating the layout with just random numbers which is not showing up with my UI (as per my use), This is what I found out in library for dynamic UI

What I am trying to do

I have this data as in my state

const [data1, setData] = useState({
    EmpData: [
        {
            empId: 11,
            lay1: { i: 'a' },
            data: [
                {
                    firstName: 'Steve',
                    lastName: 'Smith',
                },
                {
                    firstName: 'Michel',
                    lastName: 'Muner',
                },
            ],
        },
        {
            empId: 12,
            lay1: { i: 'b' },
            data: [
                {
                    firstName: 'Charles',
                    lastName: 'Johnsen',
                },
                {
                    firstName: 'Glen',
                    lastName: 'Tyson',
                },
            ],
        },
        {
            empId: 13,
            lay1: { i: 'c' },
            data: [
                {
                    firstName: 'Steve',
                    lastName: 'Smith',
                },
                {
                    firstName: 'Michel',
                    lastName: 'Muner',
                },
            ],
        },
        {
            empId: 14,
            lay1: { i: 'd' },
            data: [
                {
                    firstName: 'Steve',
                    lastName: 'Smith',
                },
                {
                    firstName: 'Michel',
                    lastName: 'Muner',
                },
            ],
        },
        {
            empId: 15,
            lay1: { i: 'e' },
            data: [
                {
                    firstName: 'Steve',
                    lastName: 'Smith',
                },
                {
                    firstName: 'Michel',
                    lastName: 'Muner',
                },
            ],
        },
    ],
});

By above data I am creating Ui as per my need

return data1.EmpData.map((li, index) => (
        <div key={index.toString()} data-grid={index}"> </div> //what should I pass here as key and layout that I am not getting
            <>
                {li.data.map((l) => (
                    <>
                        <div>{l.firstName}</div>
                        <div>{l.lastName}</div>
                    </>
                ))}
            </>
        </div>
    ));
};

Passing key and creating dynamic layout is where I am not able to figure out how can I achieve this, as this is totally new thing to me.

My UI is totally dynamic Every time I am going to get dynamic data so that much cards or divs I need to show.

Here is my code sand box what I am trying to do dynamically

Edit / Update

The answer I got from @Daniil Loban is very helpfull, but when I am trying to loop with my real time data it is looping twice, That is hapenning because my data is little bit different of what I have shown here

Actually I am not able to understand I use data1.EmpData[l.i] to get data to display when I map layout. this line.

My data

    [
{
  compId: 5,
  company_name: "Info",
  comp_data: [
    {
      empId: 1,
      empName:"steve"
    },
    {
      empId: 2,
      empName:"meg"
    }
  ]
},
{
  compId: 6,
  company_name: "wipro",
  comp_data: [
    {
      empId: 11,
      empName:"mark"
    },
    {
      empId: 12,
      empName:"Tnml"
    }
  ]
}

]

  • How data goes is like company->employ of that company->data of that company,
  • So here company can be multiple employ can be multiple similarly for data
  • What i have updated in previous data I have not mentioned the company as I thought of writing minimal code.
  • I have added full code Now and Here is my code sandbox

The issue

  • Issue is it is repeating the data, I am getting duplicate data on display

  • I ll explain here like company namme->companydata->employe name this should be the perfect but what is happening is -- companyname->the in one div both employ I a, getting and in other again I am getting both ones

Here is the image which is showing wrong data

It should be for Info there are two employ so in one grid one employ and in other one the other employ.

The issue is in generateDOM this function as layout is generated perfectly but in one Layout it is repeating all then same for others, if lenght is 3 then it is creating 3 layouts which is totally fine, but inside one layout it is agian looping and showing duplicate data

Here by I am attaching one image how it should show on UI This is whaqt I am trying to achieve

manish thakur
  • 760
  • 9
  • 35
  • 76

2 Answers2

5

I think the problem was that layout was not used, I also noticed the incorrect use of random from lodash (but this is not essential)

I use data1.EmpData[l.i] to get data to display when I map layout.

I also created a sandbox where there is a button for dynamic addition, I think this is what you wanted.

  const generateDOM = () => {
    // Generate items with properties from the layout, rather than pass the layout directly
    const layout = generateLayout();
    return _.map(layout, function (l) {
      return (
        <div key={l.i} data-grid={l} className="bg-success">
          <>
            <div className="rounded" key="?"></div>
            {data1.EmpData[l.i].data.map((l) => (
              <>
                <div>{l.firstName}</div>
                <div>{l.lastName}</div>
              </>
            ))}
          </>
        </div>
      );
    });
  };

For updated question:

enter image description here

  const generateDOM = (lo) => {
    // Generate items with properties from the layout, rather than pass the layout directly
    const layout = generateLayout();
    return _.map(layout, function (l) {
      return (
        <div key={l.i} data-grid={l} className="bg-success">
          <div>{lo[l.i].empName}</div>
          {console.log(lo, l, active_menu)}
        </div>
      );
    });
  };

And when we render, we don't need map, but just one call:

  return (
    <>
      {data1.map((d, ind) => (
        <div className={ind === active_menu ? "compNameActive" : "compName"}>
          <p onClick={() => compClick(ind)}>{d.company_name}</p>
        </div>
      ))}
      <ReactGridLayout onLayoutChange={onLayoutChange} {...props}>
        {generateDOM(data1[active_menu].comp_data)}
      </ReactGridLayout>
    </>
  );

sanbox2

Daniil Loban
  • 4,165
  • 1
  • 14
  • 20
  • I followed you code and it is working fine for making the layout, the issue I am getting is with duplicate creation of data, I have edited my post and added new `code sandbox` please check one I don't know what i am missing – manish thakur Mar 08 '21 at 08:03
  • Actually I am not able to understand `I use data1.EmpData[l.i] to get data to display when I map layout.` this line – manish thakur Mar 08 '21 at 08:35
  • Hey I wanted to ask one thing, Why this `onLayoutChange` is not triggering, I have checked yor code as well as mine, when I do in class component it works fine – manish thakur Mar 09 '21 at 04:16
  • Hi, I don't know, but if it works in a class component I would use it. – Daniil Loban Mar 09 '21 at 04:28
  • Hi I have One issue similar to this post, https://stackoverflow.com/questions/66675196/storing-layout-for-my-react-app-in-local-storage-not-working Could you please check one. – manish thakur Mar 19 '21 at 05:49
2

The layout should be an object containing the layout data, including an id i to store the relation to other data:

[
    { i: "a", x: 0, y: 0, w: 2, h: 1 },
          ^------- The id to store the relation to other data
]

You need to build the layout first, either in a way that you choose, or directly from your emp-data. Only the id i has to match your emp-data, the other values are "independant" layout data.

Note that my example requires the empId to be unique (there is duplication in your example).

E.g.:

const generateLayout = () => {
  let totalIndex = -1;
  return data1.EmpData.reduce( (accEmp, empItem, empIndex) => {
    return accEmp.concat(
      empItem.data.map( (dataItem, index) => {
        const w = _.random(1, 2);
        const h = _.random(1, 3);
        totalIndex++;
        return {
          x: (totalIndex * 2) % 12,
          y: Math.floor(totalIndex / 6),
          w: w,
          h: h,
          i: empItem.empId + '-' + index
        };
      })
    );
  }, []);
}

Then, to display the content, you can iterate over the layout data, and find the elements to display by the id i stored in the layout. E.g.:

const generateDOM = () => {
  const layout = generateLayout();

  return layout.map((ly, index) => {
    const [ empIndex, dataIndex ] = ly.i.split('-');

    const empItem = data1.EmpData.find( eI => eI.empId.toString() === empIndex );
    const dataItem = empItem.data[ dataIndex ];

    return (
      <div key={ly.i} className="bg-success">
        <div className="rounded" key="?"></div>
        <>
          <div>{dataItem.firstName}</div>
          <div>{dataItem.lastName}</div>
        </>
      </div>
    );
  });
};

I think you can also do it the other way around, i.e. iterate over your emp-data, and find the matching layout item. But I'm not sure.

kca
  • 4,856
  • 1
  • 20
  • 41
  • I have edited and given unique id, could you please share one working example. – manish thakur Mar 05 '21 at 08:35
  • It works if you just replace you functions `generateLayout` and `generateDOM` with mine. – kca Mar 06 '21 at 10:31
  • I tried but it didn't, it would be much helpfull if i got to see one working example – manish thakur Mar 06 '21 at 20:33
  • Then I am sorry, I must have misunderstood you. When I insert my code into your [codesandbox](https://codesandbox.io/s/friendly-panini-qw6j7?file=/src/App.js), it works as I expect it should. If that is still wrong, then I must have misunderstood the goal. – kca Mar 07 '21 at 17:25
  • FYI: My previous comment is not true, since the code in the linked codesandbox has changed. (sorry that I didn't check before posting the link). – kca Mar 07 '21 at 18:31
  • I have edited my post with proper details, please check if you can help. – manish thakur Mar 08 '21 at 10:15