当布局更改或数据更改时,如何确保我的 ReactJS Bumbeishvili D3-Org-Chart 正确更新?

问题描述 投票:0回答:1

我已经在 CodeSandbox 上复制并简化了我的问题,可以在此处查看:https://codesandbox.io/p/sandbox/d3-org-chart-react-function-org-chart-gvfz4l?file=%2Fsrc %2Fcomponents%2FdataToggle.js%3A9%2C11

当前的问题是...

  1. (已解决)如果顶部的交换布局按钮已被点击并且节点的展开/折叠按钮被点击,则图表默认返回其原始布局(在本例中为从左到右的布局层次结构)。第二次单击节点的展开/折叠按钮时,它将实际执行所需的展开/折叠。相关代码显示在这里...
//swapButton.js
const SwapButton = forwardRef(({ onSwapLayout }, ref) => {
  // const [isCycleLayoutOn, setIsCycleLayoutOn] = useState(false);

  const swapLayout = (event) => {
    event.preventDefault();
    onSwapLayout(event);
  };

  return (
    <div>
        <button
          className="context-menu-btns"
          style={{
            backgroundColor: "#4caf50",
            color: "white",
            borderRadius: "0.5rem",
            paddingTop: "0.5rem",
            paddingBottom: "0.5rem",
            paddingLeft: "1rem",
            paddingRight: "1rem",
          }}
          onClick={(event) => swapLayout(event)}
          ref={ref}
        >
          <FaRetweet size="2rem" />
        </button>
    </div>
  );
});

export default SwapButton;

//orgChart.js
const OrganizationalChart = (props) => {
  const d3Container = useRef(null);
  const chartRef = useRef(null);
  const [layoutIndex, setLayoutIndex] = useState(0);

  const handleSwapLayout = useCallback((event) => {
    if (event) {
      setLayoutIndex((prevIndex) => (prevIndex + 1) % 4);
      return layoutIndex;
    }
    return layoutIndex;
  });

  useLayoutEffect(() => {
    const handleLayout = () => handleSwapLayout();

    const chart = new OrgChart();
    if (props.data && d3Container.current) {
      chart
        .container(d3Container.current)
        .data(props.data)
        .nodeWidth((d) => 300)
        .nodeHeight((d) => 150)
        .layout(["left", "top", "right", "bottom"][handleLayout()])
        .compactMarginBetween((d) => 80)
        .onNodeClick((d) => {
          toggleDetailsCard(d);
        })
        .buttonContent((node, state) => {
          return ReactDOMServer.renderToStaticMarkup(
            <CustomExpandButton {...node.node} />
          );
        })
        .nodeContent((d) => {
          return ReactDOMServer.renderToStaticMarkup(
            <CustomNodeContent {...d} />
          );
        })
        .render();

      chartRef.current = chart;
    }
  }, [props, props.data, handleSwapLayout]);

  return (
    <div style={styles.orgChart} ref={d3Container}>
      <div
        style={{
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
          padding: "0.5rem",
        }}
      >
        <SwapButton onSwapLayout={handleSwapLayout} ref={d3Container} />
    </div>
  );
};

export default OrganizationalChart;
  1. 如果单击切换开关,则需要单击更改数据集按钮 3-4 次才能更新节点。当它更新时,它最终会向后更新。此外,当单击展开/折叠按钮时,它将返回到原来的布局。
//dataToggle.js
const DataToggle = ({ onDataChange, isToggleOn }) => {

  const handleChange = (event) => {
    event.preventDefault();
    onDataChange(event);
  };

  return (
    <div>
        <span className={!isToggleOn ? "label_txt_unchecked" : "label_txt"}>OFF</span>
        <label className="switch">
          <input
            checked={isToggleOn}
            type={"checkbox"}
            onChange={(event) => handleChange(event)}
          />
          <span className="slider"></span>
        </label>
        <span className={isToggleOn ? "label_txt_checked" : "label_txt"}>ON</span>
    </div>
  );
};

export default DataToggle;

//App.js
const App = () => {
  const [data, setData] = useState(employees);
  const [isToggleOn, setIsToggleOn] = useState(false);

  const handleDataChange = (event) => {
    event.preventDefault();
    setIsToggleOn(!isToggleOn);
    if (isToggleOn) {
      setData(revisedEmployees);
    } else {
      setData(employees);
    }
  };

  return (
    <React.StrictMode>
        <>
          <h1 style={styles.title}>Organization Chart</h1>
          <div>
            <DataToggle onDataChange={handleDataChange} isToggleOn={isToggleOn}/>
          </div>
          <OrganizationalChart data={data} />     
        </>
    </React.StrictMode>
  );
};//

export default App;

我怀疑这些问题与向 DOM 添加新信息而没有正确清除 DOM 中的旧信息有关。感觉就像它正在尝试渲染两个不同的图表或未正确保存/更新。

我已经尝试过使用...

  • d3-org-chart
    方法:
    .connectionsUpdate()
    .linkUpdate()
    .nodeContent()
    .nodeUpdate()
    .updateNodesState()
    .update()
    》没有成功(我什至不相信我是否正确使用了它们)。这是我获取 d3-org-chart 的地方:https://github.com/bumbeishvili/org-chart?tab=readme-ov-file

  • d3
    方法的不同组合,例如
    .select()
    .selectAll()
    .enter()
    .append()
    .join()
    .merge()
    .exit()
    .remove()
    ...全部都带有没有成功。

  • useState()
    useRef()
    useCallback()
    useEffect()
    useLayoutEffect()
    forwardRef()
    ——没有成功。

我开始这个项目的原始模板在这里:https://codesandbox.io/s/org-chart-fnx0zi?file=/src/components/orgChart.js

理想情况下,我希望图表在按钮或单击时不呈现其原始布局,除非刷新网页。此外,我希望不需要单击 3-4 次更改数据集按钮即可在正确的父节点下更新节点位置。如果有一种方法可以通过

d3-org-chart
方法完成更新,我会更喜欢。然而,考虑到我在 4 个月的时间里还没有找到解决方案,我会采取任何解决方案。

reactjs dom d3.js orgchart d3-org-chart
1个回答
0
投票

要解决问题 #1,请将

const chartRef = useRef(new OrgChart())
放在开头,然后从
const chart = new OrgChart()
中删除
useLayoutEffect()

根据bumbeishvili的作者

d3-org-chart
的说法,这是使用react时常见的错误。他在此 CodeSandbox 中解决了这个问题:

https://codesandbox.io/p/sandbox/d3-org-chart-react-function-org-chart-forked-kmsd96?file=%2Fsrc%2Fcomponents%2ForgChart.js%3A8%2C39

© www.soinside.com 2019 - 2024. All rights reserved.