Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Reactjs 基于树菜单对象的递归组件渲染_Reactjs - Fatal编程技术网

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

Reactjs 基于树菜单对象的递归组件渲染,reactjs,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:“一些价值观” }; 我想要实现的是(当我从下拉菜单中选择值时): 如果子树[value]是对

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

例如,考虑以下对象:

{
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:“一些价值观”
};
我想要实现的是(当我从下拉菜单中选择
值时):

  • 如果
    子树[value]
    是对象(
    {}
    )或数组(
    []
    ),则在 一个新的选择框下拉列表,直接显示当前
  • 否则停止
初始显示

在下拉列表中选择一个值 选择一个值后,下一个选择将显示为空,依此类推

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


我的项目的源代码位于:

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

对我来说有意义的是将路径片段存储为
数组
。这样我们就可以使用
slice
来移除尾部。我将作为一个助手来访问路径上的值。我希望prop
data
是对象本身,而不是像您以前那样的
object.entries

import React, { useState, useEffect } from "react";
import { MenuItem, TextField } from "@material-ui/core";
import _get from "lodash/get";

const InfiniteSelection = ({ data, onCaseCadeSelection }) => {
  // an array of segments like ['a1', 'x4', 'z1']
  const [path, setPath] = useState([]);

  // joins to a string like `a1.x4.z1`
  const cascade = path.join(".");

  // call callback whenever the cascade changes
  useEffect(() => {
    if (onCaseCadeSelection) {
      onCaseCadeSelection(cascade);
    }
  }, [cascade]);

  // need to know the index in the paths array where the change occurred
  const handleChange = (index) => (event) => {
    // set this value and delete everything after it
    setPath([...path.slice(0, index), event.target.value]);
  };

  // options for the NEXT value from a given path
  const optionsForPath = (path) => {
    // lodash get handles this except when path is empty array []
    const value = path.length > 0 ? _get(data, path) : data;

    // get the options from this path, or null if it is terminal
    return typeof value === "object" ? Object.keys(value) : null;
  };

  // either the current path is to a terminal value, or there should be one more level of selects
  const currentOptions = optionsForPath(path);

  // helper function can be used as a callback to path.map
  // will also be called one extra time for the next values if not on a terminal value
  const renderSelect = (value, index) => {
    return (
      <SelectControlled
        className="text form_text"
        variant="outlined"
        list={optionsForPath(path.slice(0, index)) ?? []}
        onChange={handleChange(index)}
        value={value ?? ""}
      />
    );
  };

  // render selects for each element in the path and maybe a next select
  return (
    <div className="vertically_spaced">
      {path.map(renderSelect)}
      {currentOptions === null || renderSelect("", path.length)}
    </div>
  );
};
import React,{useState,useffect}来自“React”;
从“@material ui/core”导入{MenuItem,TextField}”;
从“lodash/get”导入;
常量无限选择=({data,onCaseCadeSelection})=>{
//类似于['a1'、'x4'、'z1'的段数组
const[path,setPath]=useState([]);
//连接到类似'a1.x4.z1'的字符串`
const cascade=path.join(“.”);
//每当级联更改时调用callback
useffect(()=>{
if(onCaseCadeSelection){
onCaseCadeSelection(级联);
}
},[cascade]);
//需要知道发生更改的路径数组中的索引
常量句柄更改=(索引)=>(事件)=>{
//设置此值并删除它之后的所有内容
setPath([…path.slice(0,索引),event.target.value]);
};
//指定路径中下一个值的选项
const options路径=(路径)=>{
//lodash get将处理此问题,除非路径为空数组[]
const value=path.length>0?\u get(数据,路径):数据;
//从该路径获取选项,如果为terminal,则为null
返回typeof值===“对象”?对象。键(值):null;
};
//要么当前路径指向一个终端值,要么应该有一个以上级别的选择
const currentOptions=路径选项(路径);
//helper函数可用作path.map的回调函数
//如果不是在终端值上,也将为下一个值额外调用一次
常量renderSelect=(值,索引)=>{
返回(
);
};
//渲染为路径中的每个元素选择,可能还有下一个选择
返回(
{path.map(renderSelect)}
{currentOptions==null | | renderSelect(“,path.length)}
);
};

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

对我来说有意义的是将路径片段存储为
数组
。这样我们就可以使用
slice
来移除尾部。我将作为一个助手来访问路径上的值。我希望prop
data
是对象本身,而不是像您以前那样的
object.entries

import React, { useState, useEffect } from "react";
import { MenuItem, TextField } from "@material-ui/core";
import _get from "lodash/get";

const InfiniteSelection = ({ data, onCaseCadeSelection }) => {
  // an array of segments like ['a1', 'x4', 'z1']
  const [path, setPath] = useState([]);

  // joins to a string like `a1.x4.z1`
  const cascade = path.join(".");

  // call callback whenever the cascade changes
  useEffect(() => {
    if (onCaseCadeSelection) {
      onCaseCadeSelection(cascade);
    }
  }, [cascade]);

  // need to know the index in the paths array where the change occurred
  const handleChange = (index) => (event) => {
    // set this value and delete everything after it
    setPath([...path.slice(0, index), event.target.value]);
  };

  // options for the NEXT value from a given path
  const optionsForPath = (path) => {
    // lodash get handles this except when path is empty array []
    const value = path.length > 0 ? _get(data, path) : data;

    // get the options from this path, or null if it is terminal
    return typeof value === "object" ? Object.keys(value) : null;
  };

  // either the current path is to a terminal value, or there should be one more level of selects
  const currentOptions = optionsForPath(path);

  // helper function can be used as a callback to path.map
  // will also be called one extra time for the next values if not on a terminal value
  const renderSelect = (value, index) => {
    return (
      <SelectControlled
        className="text form_text"
        variant="outlined"
        list={optionsForPath(path.slice(0, index)) ?? []}
        onChange={handleChange(index)}
        value={value ?? ""}
      />
    );
  };

  // render selects for each element in the path and maybe a next select
  return (
    <div className="vertically_spaced">
      {path.map(renderSelect)}
      {currentOptions === null || renderSelect("", path.length)}
    </div>
  );
};
import React,{useState,useffect}来自“React”;
从“@material ui/core”导入{MenuItem,TextField}”;
从“lodash/get”导入;
常量无限选择=({data,onCaseCadeSelection})=>{
//类似于['a1'、'x4'、'z1'的段数组
const[path,setPath]=useState([]);
//连接到类似'a1.x4.z1'的字符串`
const cascade=path.join(“.”);
//每当级联更改时调用callback
useffect(()=>{
if(onCaseCadeSelection){
onCaseCadeSelection(级联);
}
},[cascade]);
//需要知道发生更改的路径数组中的索引
常量句柄更改=(索引)=>(事件)=>{
//设置此值并删除它之后的所有内容
setPath([…path.slice(0,索引),event.target.value]);
};
//指定路径中下一个值的选项
const options路径=(路径)=>{
//lodash get将处理此问题,除非路径为空数组[]
const value=path.length>0?\u get(数据,路径):数据;
//从该路径获取选项,如果为terminal,则为null
返回typeof值===“对象”?对象。键(值):null;
};
//要么当前路径指向一个终端值,要么应该有一个以上级别的选择
const currentOptions=路径选项(路径);
//helper函数可用作path.map的回调函数
//如果不是在终端值上,也将为下一个值额外调用一次
常量renderSelect=(值,索引)=>{
返回(
);
};
//渲染为路径中的每个元素选择,可能还有下一个选择
返回(
{path.map(renderSelect)}
{currentOptions==null | | renderSelect(“,path.length)}
);
};

来自@LindaPaiste的回答:


更改不是路径中最后一个值的值时,需要清除所有Subsque
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;
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
);