在 react-grid-layout 的布局对象中切换静态键布尔值会导致所有网格项目移动不正确

Toggling the static key boolean in layout object of a react-grid-layout causes all grid items to move incorrectly

tl;博士:

React-grid-layout incorrectly moves all grid items around when their static option is enabled and disabled (toggled). I want the grid items to be frozen in place when static, and move if and only if the user moves them with static mode disabled. An example of the problem can be found here (CodeSandbox), with a GIF example here (Imgur).


背景

我正在使用 react-grid-layout. I have a button to toggle between non-static (draggable and resizeable) and static (non-interactive) mode for all items in the grid (info about static in react-grid-layout). I am doing so with useState and with tips from :

为我的应用制作交互式网格布局
var [layout, setLayout] = useState([
  { i: "a", x: 0, y: 0, w: 10, h: 4, static: false },
  { i: "b", x: 1, y: 0, w: 3, h: 2, static: false },
  { i: "c", x: 4, y: 0, w: 1, h: 2, static: false },
]);

function toggleStaticMode() {
  setLayout(
    layout.map((prevLayout) => {
      return { ...prevLayout, static: !prevLayout.static };
    })
  );
}

预期行为

网格中的每个元素都具有定义其在网格布局上的位置、大小等的独特属性。这些在上面显示的 layout 变量中定义。在 staticnon-static 模式之间切换时,所有值都应保持不变,每个对象中的 static 键除外。屏幕上的位置和大小也应保持不变。这使用户能够关闭 static 模式以更改网格项目的 position/size,然后将它们“冻结”在他们离开的 place/size 中。

当前(错误)行为:

我的问题是当我处于 非静态 模式时,我将网格项目拖到我想要的位置,然后切换 static 模式,他们重置到他们之前的位置。即:

我将元素 A 拖到堆栈的底部,当我打开静态模式时,它会移动到堆栈的中间,然后将其关闭 returns 元素 A到堆栈的顶部。然后,元素 A 将在中间和顶部之间循环,具体取决于静态模式是打开还是关闭。

这是我的意思的一个例子:


我试过的:

上面的

None 告诉我发生了什么或为什么会这样。文档也没有提供太多相关信息。

一个可能有效的“hackish”解决方案是使用本地 storage/cookies from this example,但我不想在用户每次切换静态模式时都这样做。此解决方案实际上可能无法解决此问题。

这可能是一个错误,但如果可能,我想找到更好的解决方法。


已使用完整代码:

import React, {
  useState
} from "react";
import GridLayout from "react-grid-layout";

export default function Home() {
  let [layout, setLayout] = useState([{
      i: "a",
      x: 1,
      y: 0,
      w: 8,
      h: 4,
      static: false
    },
    {
      i: "b",
      x: 3,
      y: 2,
      w: 3,
      h: 2,
      static: false
    },
    {
      i: "c",
      x: 5,
      y: 4,
      w: 1,
      h: 2,
      static: false
    },
  ]);

  function printLayoutItem(val) {
    console.log(layout[val].i + ": (w: " + layout[val].w + " h: " + layout[val].h + " x: " + layout[val].x + " y: " + layout[val].y + ")");
  }

  function toggleStaticMode() {
    setLayout(
      layout.map((l) => {
        return { ...l,
          static: !l.static
        };
      })
    );
  }

  // let [isStatic, setStatic] = useState(false);

  // function toggleStaticMode() {
  //    if (isStatic) setStatic(false) 
  //    else setStatic(true);
  //    printLayoutItem(0);
  //    printLayoutItem(1);
  //    printLayoutItem(2);
  //    setLayout([
  //        { i: "a", x: 3, y: 0, w: 8, h: 4, static: isStatic },
  //        { i: "b", x: 0, y: 2, w: 3, h: 2, static:  isStatic},
  //        { i: "c", x: 5, y: 4, w: 1, h: 2, static: isStatic },
  //    ]);
  // }

  return ( <div>
    <main>{/* thanks for this beautiful Tidy, Whosebug */}
    <button onClick ={toggleStaticMode}>
    Toggle Static Mode
    </button> 
    <GridLayout 
        className="layout"
        layout={layout}
        cols={4}
        rowHeight={100}
        width={1200}>
    <div key="a">Hello A </div> 
    <div key="b">Hello B </div> 
    <div key="c">Hello C </div> 
    </GridLayout > 
   </main> 
   </div>
  );
}
<!-- Snippet does not run correctly, please see the example below for a working example of the error occuring -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

我还 uploaded this code to CodeSandbox 展示我当前问题的一个完整的、有效的例子。 此示例在使用 React classes/states 而不是 useState 的方式上 不同 ,但会出现相同的问题。此示例显示切换静态模式时 size/position 值发生变化。

编辑:

Someone Special's answer got me in the right direction, however, when the columns rearrange to a smaller value than the minimum size, the items scatter upon toggle again. An example of this can be viewed here.

更新:找到您的问题。您更新了 lg 数组,但没有更新 sm 数组。

toggleStaticMode() {
    this.setState({
      layouts: {
        lg: this.state.layouts.lg.map((l) => {
          return { ...l, static: !l.static };
        }),
        sm: this.state.layouts.sm.map((l) => {
          return { ...l, static: !l.static };
        })
      }
    });
  }

通过在 togglestaticmode 中更新 sm 数组,它在我这边有效。 Sandbox


之前:

我以前没有使用过这个 react-grid-layout,但是我看到你的 onLayout 函数如下。

  onLayoutChange(layout, layouts) {
    this.props.onLayoutChange(layout, layouts);
  }

您使用 props 更新了您的布局,并设置了 initialprops,但这只会设置 initialProps - 它不会侦听父组件对 props 的更改。

意思是,当你使用props.onLayoutChange(从你的父组件)时,你的父组件改变了,但你的组件状态没有改变。

你可以,

  1. 除了 props.onLayoutChange
  2. 之外,还要在 onLayoutChange 中更新您的状态
  3. 使用 componentDidUpdate 更新您的状态,使其与您的父组件同步。

componentDidUpdate