基于树形菜单对象的递归组件渲染

时间:2021-02-24 19:29:37

标签: reactjs

我创建了一个组件,当对象内的键是另一个对象时,它会添加一个额外的选择框下拉列表。

例如,考虑以下对象:

{
  a1: {
    x1: 1,
    x2: 2,
    x3: 3,
    x4: {
      z1: "z1",
      z2: "z2"
    },
    x5: [
      {
        x5a: {
          z5a1: 1,
          z5a2: 2
        }
      },
      {
        x5b: {
          z5b1: 1,
          z5b2: 2
        }
      }
    ]
  },
  a2: {
    x1: 1,
    x2: 2,
    x3: 3
  },
  a3: "some values"
};

我想要实现的是(当我从下拉菜单中选择 value 时):

  • 如果 subTree[value] 是一个对象 ({}) 或一个数组 ([]),在 一个新的选择框下拉,直接在当前的下方
  • 否则停止

初始显示

enter image description here

在下拉列表中选择一个值

选择一个值后,下一个选择将显示为空,依此类推...

enter image description here

enter image description here

问题

当我更新选择框中的值时,我的代码不会正确更新/清除下面的选择。

enter image description here

我的项目的源代码位于:https://codesandbox.io/s/frosty-grass-9jdue

2 个答案:

答案 0 :(得分:1)

当更改不是路径中最后一个值时,您需要清除所有后续选择,因为它们基于不同的路径。我不太确定我们在你的设置中如何做到这一点,因为我还没有完全理解它。

对我来说有意义的是将路径的各个部分存储为 # module_b/pack_b.py from module_a import AClass1 # <-, I get an issue here 。这样我们就可以使用 array 去除尾部。我将使用 lodash's get method 作为助手来访问路径上的值。我希望道具 slice 是对象本身,而不是像您之前所做的那样 data

Object.entries

Code Sandbox Link

答案 1 :(得分:1)

来自@LindaPaiste 的回答:

<块引用>

当更改不是路径中最后一个值时,您需要清除所有后续选择,因为它们基于不同的路径。

这是解决问题的关键!您必须以某种方式吹走并忘记您当前正在更改其 value 的选择框下方的所有内容。

React 是围绕 "blow away and forget" principle 设计的。还要注意 The Data Flows Down。考虑到这一点,您的任务应该很容易完成,虽然 Linda 的解决方案似乎有效,但它可能并不那么简单。


如果我们可以有一个特殊的组件,(1) 接受数据的子树,(2) 将其第一级子项呈现为选择框下拉列表,然后 (3) 递归地重复该过程,该怎么办?像这样:

<RecursiveComponent subTree={DATA_SAMPLE} {/*maybe some other props*/}/>

当我们想到递归时,我们必须想到终止条件。在我们的例子中,当子树是原始类型(即不是对象 ({}) 或数组 ([]))时,会发生这种情况。

每个 RecursiveComponent 都必须:

  • 呈现选择菜单下拉菜单,包含子树的所有第一级子项
  • 渲染嵌套的 RecursiveComponent,基于 props.subTree[selection]
  • 处理用户交互

像这样:

import { MenuItem, Select } from "@material-ui/core";
import { useState } from "react";

function RecursiveComponent(props) {
  const [selection, setSelection] = useState(props.currentSelection);
  const handleChange = (event) => {
    setSelection(event.target.value);
  };
  return (
    <>
      <Select variant="outlined" value={selection} onChange={handleChange}>
        {Object.keys(props.subTree).map((key) => (
          <MenuItem value={key}>{key}</MenuItem>
        ))}
      </Select>
      <div /> {/* forces a line break between selection boxes */}
      {props.subTree[selection] !== Object(props.subTree[selection]) ? (
        <></>
      ) : (
        <RecursiveComponent
          subTree={props.subTree[selection]}
          currentSelection=""
        />
      )}
    </>
  );
}
export default RecursiveComponent;

这是通过编辑 RecursiveComponent 在项目中使用 index.js 的方法:

import { StrictMode } from "react";
import ReactDOM from "react-dom";

import { DATA_SAMPLE } from "./DataSample";
import RecursiveComponent from "./RecursiveComponent";

const rootElement = document.getElementById("root");

ReactDOM.render(
  <StrictMode>
    <RecursiveComponent subTree={DATA_SAMPLE} currentSelection="" />
  </StrictMode>,
  rootElement
);