Reputation: 837
I have a form that I used material ui and formik to implement it. Inside, I need to have a selection box, with nested options, it should look like this eventually. (Then I should get the value selected and submit form )
For simplicity: I only want recursive children items rendering from a json file:
I have a json file that is nested with children objects. Sample:
[
{
"label": "Bavullar ve \u00c7antalar",
"value": 5181.0,
"children": [
{
"label": "Al\u0131\u015fveri\u015f \u00c7antalar\u0131",
"value": 5608.0
},
{
"label": "Bavul Aksesuarlar\u0131",
"value": 110.0,
"children": [
{
"label": "Korumal\u0131 Kap D\u00fczenleyicileri ve B\u00f6lme Ekleri",
"value": 503014.0
},
{
"label": "Seyahat Keseleri",
"value": 5650.0
},
{
"label": "Seyahat \u015ei\u015fe ve Kaplar\u0131",
"value": 6919.0
}
]
},
{
"label": "Bebek Bezi \u00c7antalar\u0131",
"value": 549.0
},
{
"label": "Bel \u00c7antalar\u0131",
"value": 104.0
},
{
"label": "Elbise \u00c7antalar\u0131",
"value": 105.0
}
...
This is the form I need to insert my dropdown.
// import categories from json file
import categories from '../gpc.tr.json'
// Form component
<form onSubmit={formik.handleSubmit} className={classes.root} id='form'>
<FormhelperText> <span style={{color:'red'}}>*</span> Set Google Product Category ID to </FormhelperText>
<Select id="category" label="Set Google Product Category ID to" fullWidth variant="outlined" name="schema.category"
value={formik.values.schema.category}
onChange={formik.handleChange}
error={formik.touched?.schema?.category && Boolean(formik.errors.category)}
helpertext={formik.touched?.schema?.category && formik.errors.category}>
// HERE I TRY TO INSERT A COMPONENT FOR DROPDOWN
{renderCategories(categories)}
</Select>
so in renderCategories component, I use recursion to render nested children. But I couldn't make it work.
const renderCategories = (categories) => {
return (<div>
{categories.map(i => {
return <MenuItem key={i.value} value={i.value}>
{ i.label }
{ i.children && renderCategories(i.children) }
</MenuItem>
})}
</div>
)}
I get <li> cannot appear as a descendant of <li>.
warning and all of the children elements appear all at once. Like in the picture. Even if I style it and hide children elements, I feel like there is a problem here, it gets really slow to render it. How should I solve this problem? Is there a better way to implement it?
Here is the sandbox link: https://codesandbox.io/s/empty-moon-4diw4?file=/src/ProductForm.js
UPDATE
I used NestedMenuItem from "material-ui-nested-menu-item" package. But still, my recursive function doesn't work right.
function renderCategories (categories) {
return categories.map((i) => {
return(
<NestedMenuItem key={i.value} value={i.value}
label={i.label}
parentMenuOpen={!!menuPosition}
onClick={handleItemClick}>
<MenuItem
component={'div'}
onClick={handleItemClick}
key={i.value}> { i.label }
</MenuItem>
{ i.children && renderCategories(i.children) }
</NestedMenuItem>
)
})
}
I get lost here, it doesn't render it properly. how would you implement a recursive dropdown list inside a form in react?
Upvotes: 3
Views: 7949
Reputation: 487
I would use ant design cascader for such a big data set. https://ant.design/components/cascader/ There are different components you can use and you don't need a recursion just pass your json as options. Sample usage:
import { Cascader } from 'antd';
const options = [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{
value: 'xihu',
label: 'West Lake',
},
],
},
],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [
{
value: 'nanjing',
label: 'Nanjing',
children: [
{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
},
],
},
],
},
];
function onChange(value) {
console.log(value);
}
ReactDOM.render(
<Cascader options={options} onChange={onChange} placeholder="Please select" />,
mountNode,
);
Upvotes: 1
Reputation: 2968
If you pass component="div"
to the MenuItem
component, the error that you faced would be removed. sandbox
<MenuItem
onClick={() => console.log(123)}
component="div"
key={i.value}
value={i.value}
>
Upvotes: 0
Reputation: 1318
If you want to do nested lists, MenuItem
is not the right component for this.
It's using the li
tag as a base and this is why you're getting this warning.
I suggest you use an external package for this, material-ui-nested-menu-item, created exactly for this.
All you have to do is to replace MenuItem
by its default NestedMenuItem
component, wrap them with a Menu
container and use the container ref:
<Menu
open={!!menuPosition}
onClose={() => setMenuPosition(null)}
anchorReference="anchorPosition"
anchorPosition={menuPosition}
>
<MenuItem onClick={handleItemClick}>Button 1</MenuItem>
<MenuItem onClick={handleItemClick}>Button 2</MenuItem>
<NestedMenuItem
label="Button 3"
parentMenuOpen={!!menuPosition}
onClick={handleItemClick}
>
<MenuItem onClick={handleItemClick}>Sub-Button 1</MenuItem>
<MenuItem onClick={handleItemClick}>Sub-Button 2</MenuItem>
<NestedMenuItem
label="Sub-Button 3"
parentMenuOpen={!!menuPosition}
onClick={handleItemClick}
>
<MenuItem onClick={handleItemClick}>Sub-Sub-Button 1</MenuItem>
<MenuItem onClick={handleItemClick}>Sub-Sub-Button 2</MenuItem>
</NestedMenuItem>
</NestedMenuItem>
<MenuItem onClick={handleItemClick}>Button 4</MenuItem>
<NestedMenuItem
label="Button 5"
parentMenuOpen={!!menuPosition}
onClick={handleItemClick}
>
<MenuItem onClick={handleItemClick}>Sub-Button 1</MenuItem>
<MenuItem onClick={handleItemClick}>Sub-Button 2</MenuItem>
</NestedMenuItem>
</Menu>
Click here to see a CodeSandbox of its usage.
Another solution would be to make MenuItem
use a different tag as base by providing the component
prop.
Upvotes: 1