如何使用 react-grid-layout 创建动态拖放布局

IT技术 javascript html reactjs grid-layout react-grid-layout
2021-04-16 04:22:14

我正在使用react-grid-layout来制作drag-drop and resize components,所以我能够实现文档中提到的功能。

我的问题

  • 我正在创建动态 UI,所以我正在用数据渲染我的 UI。
  • 在这个库中,我需要创建布局,例如 { {i: 'a', x: 0, y: 0, w: 1, h: 2}
  • 当我创建静态 UI 时,我能够实现我想要的。这是工作代码沙箱

在具有预定义布局的静态 Ui 中,我喜欢下面的内容。

    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>

这工作正常,但这里我定义的布局是静态的,因此为此我搜索并获得了一个动态高度和宽度的示例,它正在创建动态布局。

但这只是用随机数创建布局,这些数字没有显示在我的 UI 中(根据我的使用),这是我在动态 UI 库中发现的

我正在尝试做什么

我在我的州有这些数据

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',
                },
            ],
        },
    ],
});

通过以上数据,我根据需要创建了 Ui

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>
    ));
};

传递key和创造动态layout是我无法弄清楚如何实现这一目标的地方,因为这对我来说是全新的事情。

我的 UI 是完全动态的每次我要获取动态数据时,我都需要显示很多卡片或 div。

这是我的代码沙箱,我正在尝试动态执行此操作

编辑/更新

我得到的答案@Daniil Loban非常有帮助,但是当我尝试使用实时数据进行循环时,它会循环两次,这是因为我的数据与我在此处显示的数据略有不同

其实我无法理解I use data1.EmpData[l.i] to get data to display when I map layout.这一行。

我的资料

    [
{
  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"
    }
  ]
}

]

  • 数据的流向就像公司->该公司的雇员->该公司的数据,
  • 所以这里的公司可以是多个员工,数据也可以是多个类似的
  • 我在以前的数据中更新的内容我没有提到公司,因为我想到了编写最少的代码。
  • 我现在添加了完整的代码,这是我的代码沙箱

问题

  • 问题是它在重复数据,我正在显示重复的数据

  • 我会在这里解释, company namme->companydata->employe name这应该是完美的,但正在发生的事情是——公司名称->在一个 div 中都使用了我 a,在另一个 div 中,我同时获得了两个

这是显示错误数据的图像

应该是因为Info有两个雇用,所以在一个网格中一个雇用,另一个雇用另一个。

问题在generateDOM于此功能,因为布局是完美生成的,但在一个布局中,它对其他布局重复所有相同的内容,如果长度为 3,则它正在创建 3 个完全没问题的布局,但在一个布局中,它再次循环并显示重复数据

在这里,我附上了一张图片,它应该如何在 UI 上显示 这就是我想要实现的目标

2个回答

我认为问题在于没有使用布局,我还注意到 lodash 中 random 的使用不正确(但这不是必需的)

我用来data1.EmpData[l.i]在映射时获取要显示的数据layout

我还创建了一个沙箱,其中有一个用于动态添加的按钮,我认为这就是您想要的。

  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>
      );
    });
  };

对于更新的问题:

在此处输入图片说明

  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>
      );
    });
  };

当我们渲染时,we don't need map,但只是一个调用:

  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

嗨,我不知道,但如果它在类组件中工作,我会使用它。
2021-05-26 04:22:14
我跟着你的代码,它可以很好地制作布局,我遇到的问题是重复创建数据,我已经编辑了我的帖子并添加了新的code sandbox请检查一个我不知道我错过了什么
2021-05-27 04:22:14
嘿,我想问一件事,为什么这onLayoutChange不会触发,我已经检查了您的代码以及我的代码,当我在类组件中执行时它工作正常
2021-05-31 04:22:14
嗨,我有一个类似于这篇文章的问题,stackoverflow.com/questions/ 66675196/ ……你能检查一下吗。
2021-06-07 04:22:14
其实我无法理解I use data1.EmpData[l.i] to get data to display when I map layout.这一行
2021-06-12 04:22:14

布局应该是一个包含布局数据的对象,包括一个 idi来存储与其他数据的关系:

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

您需要首先以您选择的方式或直接从您的 emp-data 构建布局。只有idi必须与您的 emp-data匹配,其他值是“独立”布局数据。

请注意,我的示例要求empId是唯一的(您的示例中有重复)。

例如:

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
        };
      })
    );
  }, []);
}

然后,为了显示内容,您可以遍历布局数据,并通过i存储在布局中的 id 找到要显示的元素例如:

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>
    );
  });
};

我认为你也可以反过来做,即迭代你的 emp-data,并找到匹配的布局项。但我不确定。

如果你只是用我的功能替换你的功能generateLayout它就会起作用generateDOM
2021-05-23 04:22:14
我试过了,但没有,如果我能看到一个工作示例会很有帮助
2021-06-04 04:22:14
我已经编辑并给出了唯一的 id,请您分享一个工作示例。
2021-06-12 04:22:14
那对不起,我一定是误会了你。当我将我的代码插入您的代码和框时,它会按我预期的那样工作。如果那仍然是错误的,那么我一定误解了目标。
2021-06-15 04:22:14
仅供参考:我之前的评论是不正确的,因为链接代码和框中的代码已更改。(抱歉,我在发布链接之前没有检查)。
2021-06-18 04:22:14