我正在构建一个 UI,用于使用“嵌套”自动完成组件按类别过滤产品。
我有一个面包屑来显示当前选择的类别,并自动完成以选择当前级别中的类别。
类别由不同深度的树表示(最多可达 20 个),例如:
Women > Accessories > Glasses
Women > Underwear
Men > Shoes > Sandals
在上面的示例中,有 3 个类别级别(但可以不止这个)。
首先,用户可以从选项中进行选择
[Women, Men]
。然后,他可以从所选选项的第二级中选择一个选项。如果用户选择 Women
,他现在可以在 [Accessories, Underwear]
之间进行选择。面包屑将相应更新。
我的问题是,选定的选项在选择后保留,而我在幕后更改选项列表。这会产生以下警告(我想摆脱它):
MUI: The value provided to Autocomplete is invalid. None of the options match with `"Women"`.You can use the `isOptionEqualToValue` prop to customize the equality test.
此外,“最后”选择的选项保留在文本框中,我希望它为空以供新选择(不同类别级别)。我该怎么做?
这里是一个代码沙箱,显示了我的用例。
type CategoryLevel = {
[key: string]: CategoryLevel;
};
function getCurrentCategoriesList(
categoriesTree: CategoryLevel,
categories: string[]
) {
let subTree = categoriesTree;
for (const category of categories) {
subTree = subTree[category] || {};
}
if (!subTree) {
return [];
}
return Object.keys(subTree);
}
export default function App() {
const [open, setOpen] = useState(false);
const [options, setOptions] = useState<string[]>([]);
const [categories, setCategories] = useState<string[]>([]);
useEffect(() => {
const newOptions = getCurrentCategoriesList(categoriesTree, categories);
setOptions(newOptions);
}, [categories]);
function handleCategoryChange(_event, newValue: string | null) {
if (newValue != null) {
setCategories([...categories, newValue]);
}
}
return (
<Box className="App">
<h1>Hello Categories</h1>
<h2>
<Button onClick={() => setCategories([])}>Reset</Button>
{categories.join(" > ")}
</h2>
<FormControl>
<Autocomplete
disableClearable
open={open}
sx={{ width: 300 }}
onClose={() => {
setOpen(false);
}}
onOpen={() => {
setOpen(true);
}}
noOptionsText="No Categories"
options={options}
onChange={handleCategoryChange}
// onInputChange={handleInputChange}
renderInput={(params) => (
<TextField
{...params}
label={
getCurrentCategoriesList(categoriesTree, categories).length > 0
? "Category"
: "NA"
}
variant="standard"
/>
)}
/>
</FormControl>
</Box>
);
}
categoriesTree = {
Men: {
"T-Shirts": {
Polos: {},
},
Shorts: {},
Jackets: {},
Accessories: {
Glasses: {},
Belts: {},
Underwear: {},
Socks: {},
},
},
Women: {
"T-Shirts": {
Polos: {},
},
Shorts: {},
Jackets: {},
Accessories: {
Glasses: {},
Belts: {},
Underwear: {},
Socks: {},
},
},
};
我的解决方案是使用Free Solo:
旨在涵盖搜索输入的主要用例 建议
所以我最终进行了以下更改:
freeSolo
添加 Autocomplete
道具。searchValue
状态来存储 value
和 inputValue
道具的当前值。onChange
本身的 TextField
中的值(而不是在 Autocomplete
事件中)。更新代码:
type CategoryLevel = {
[key: string]: CategoryLevel;
};
function getCurrentCategoriesList(
categoriesTree: CategoryLevel,
categories: string[]
) {
let subTree = categoriesTree;
for (const category of categories) {
subTree = subTree[category] || {};
}
if (!subTree) {
return [];
}
return Object.keys(subTree);
}
export default function App() {
const [open, setOpen] = useState(false);
const [options, setOptions] = useState<string[]>([]);
const [categories, setCategories] = useState<string[]>([]);
const [searchValue, setSearchValue] = useState("");
useEffect(() => {
const newOptions = getCurrentCategoriesList(categoriesTree, categories);
setOptions(newOptions);
}, [categories]);
function handleCategoryChange(_event, newValue: string | null) {
if (newValue != null) {
setCategories([...categories, newValue]);
setSearchValue("");
}
}
return (
<Box className="App">
<h1>Hello Categories</h1>
<h2>
<Button onClick={() => setCategories([])}>Reset</Button>
{categories.join(" > ")}
</h2>
<FormControl>
<Autocomplete
freeSolo
disableClearable
value={searchValue}
inputValue={searchValue}
disabled={options.length === 0}
open={open}
sx={{ width: 300 }}
onClose={() => {
setOpen(false);
}}
onOpen={() => {
setOpen(true);
}}
noOptionsText="No Categories"
options={options}
onChange={handleCategoryChange}
renderInput={(params) => (
<TextField
{...params}
label={
getCurrentCategoriesList(categoriesTree, categories).length > 0
? "Category"
: "NA"
}
variant="standard"
onChange={(event) => {
// don't fire API if the user delete or not entered anything
if (event.target.value !== null) {
setSearchValue(event.target.value);
}
}}
/>
)}
/>
</FormControl>
</Box>
);
}