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
来移除尾部。我将作为一个助手来访问路径上的值。我希望propdata
是对象本身,而不是像您以前那样的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
来移除尾部。我将作为一个助手来访问路径上的值。我希望propdata
是对象本身,而不是像您以前那样的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
);