如何从数据库恢复Material UI Autocomplete已选选项并显示
问题描述
我需要将数据库中保存的已选选项在Material UI Autocomplete组件中以标签形式展示,目前已成功从数据库获取选项列表,但已选选项仅显示空标签。相关React组件代码如下:
PostInfo组件代码
import SelectPostsCatsOptions from "../../components/autocomplete/SelectPostsCatsOptions"; export default function PostInfo() { const [categories, setCategories] = useState([]); const [selectedCategories, setSelectedCategories] = useState([]); const [postInfoPageUpdateMode, setPostInfoPageUpdateMode] = useState(false); // TO FETCH DATA FROM THE DATABASE useEffect(() => { // TO FETCH POST const getPost = async () => { try { const res = await axios.get("/posts/" + path); setPost(res.data); setCategories(res.data.categories); setIsLoading(false); // HANDLING ERROR } catch (err) { .. } // END OF HANDLING ERROR }; getPost() }, [path]); // END OF TO FETCH DATA FROM THE DATABASE // HANDLE THE DATA TO SUBMIT const handleUpdate = async () => { if (!title || !content || !photo ) { setAlertMsg({ fail: true, success: false, msg: "All fields are required!" }); return; } else try { await axios.put(`/posts/${post._id}`, { username:user.username, title, content, photo, categories: selectedCategories.map(cat => cat.catName), }); window.location.reload(); setPostInfoPageUpdateMode(false); // HANDLING ERROR } catch (err) { setAlertMsg({ fail: true, success: false, msg: err.response.data.message }); console.log("Updating Error:", err); } // END OF HANDLING ERROR }; // END OF HANDLE THE DATA TO SUBMIT return ( // // POST INFO PAGE <div className="PostInfoPage"> {/* IF THE PAGE IS LOADING SHOW CIRCULAR PROGRESS */} {isLoading ? ( ……… ) : ( <> {/* IF THE PAGE IS ON UPDATE MODE */} {postInfoPageUpdateMode ? ( // SAVE & DISCARD BUTTON CONTAINER <div className="PostInfoPageUpdateIconButtonContainer"> ………. </div> // END OF SAVE & DISCARD BUTTON CONTAINER ) : ( )} {/* END OF IF THE PAGE IS ON UPDATE MODE */} <div className="PostInfoPageTop"> </div> {/* POST VIEW OR EDIT CONTAINER */} <div className="PostInfoPageBottom" dir="auto"> {/* POST FORM */} <form className="PostInfoPageForm" dir="auto"> {/* POST INFO TITLE IMAGE UPLOAD CONTAINER */} <div className="PostInfoPageTitleImageUploadCont"> </div> {/* END OF POST INFO TITLE IMAGE UPLOAD CONTAINER */} {/* POST CATEGORIES CONTAINER */} <div className="PostInfoPagePostContentContainer"> <h3>Post Categories:</h3> <SelectPostsCatsOptions selectedCategories={categories} setSelectedCategories={setSelectedCategories} readOnly={postInfoPageUpdateMode ? false : true} /> </div> {/* END OF POST CATEGORIES CONTAINER */} </form> {/* END OF POST FORM */} </div> {/* END OF POST VIEW OR EDIT CONTAINER */} </> )} {/* END OF IF THE PAGE IS LOADING SHOW CIRCULAR PROGRESS */} </div> // // END OF POST INFO PAGE ); }
SelectPostsCatsOptions组件代码
export default function SelectPostsCatsOptions({onBlur, selectedCategories, setSelectedCategories, readOnly}) { const [postsCats, setPostsCats] = useState([]); const [loading, setLoading] = useState(true); // FETCH THE CATEGORIES FROM THE DATABASE useEffect(() => { const getPostsCats = async () => { const res = await axios.get("/postscategories"); setPostsCats(res.data); setLoading(false); console.log("Categories Are:", res.data); }; getPostsCats() }, []); // END OF FETCH THE CATEGORIES FROM THE DATABASE return ( <div> <Autocomplete readOnly={readOnly} multiple disablePortal id="postsCats" getOptionLabel={(postsCats) => `${postsCats.catName}`} options={postsCats} isLoading={loading} loadingText="Loading categories..." maxWidth={false} disableGutters isOptionEqualToValue={(option, value) => option.catName === value.catName} noOptionsText="No Available Options" renderOption={(props, postsCats) => ( <li {...props} key={postsCats.id} style={{ … }}> {postsCats.catName} </li> )} // THE TAGS SELECTED renderTags={(value, getTagProps) => value.map((option, index) => ( <Chip key={option.id} label={option.catName} {...getTagProps({ index })} style={{…}} /> )) } // END OF THE TAGS SELECTED renderInput={(params) => <TextField readOnly={readOnly} {...params} placeholder="Categories" InputLabelProps={{ style: { … } }} sx={{…}} // THE RIGHT END ICON InputProps={{ style: { … }, ...params.InputProps, endAdornment: ( <React.Fragment> {loading ? <CircularProgress /> : null} {params.InputProps.endAdornment} </React.Fragment> ), }} // END OF THE RIGHT END ICON /> } value={selectedCategories} onChange={(event, newSelectedCategories) => setSelectedCategories(newSelectedCategories)} /> </div> ); }
解决方案
空标签的核心原因是:你从数据库获取的categories是字符串数组(提交时只存了分类名称),但Material UI Autocomplete的value期望的是和options结构一致的完整对象(包含catName、id等属性),导致渲染标签时option.catName取不到值。
修复步骤
1. 在SelectPostsCatsOptions组件中添加转换逻辑
当分类列表加载完成后,将传入的字符串分类名称转换为对应的完整对象数组:
export default function SelectPostsCatsOptions({onBlur, selectedCategories, setSelectedCategories, readOnly}) { const [postsCats, setPostsCats] = useState([]); const [loading, setLoading] = useState(true); // FETCH THE CATEGORIES FROM THE DATABASE useEffect(() => { const getPostsCats = async () => { const res = await axios.get("/postscategories"); setPostsCats(res.data); setLoading(false); console.log("Categories Are:", res.data); }; getPostsCats() }, []); // 新增:将字符串数组转换为匹配的对象数组 useEffect(() => { if (!loading && selectedCategories.length > 0) { // 判断传入的是否为字符串数组 const isStringArray = typeof selectedCategories[0] === 'string'; if (isStringArray) { const convertedCategories = selectedCategories.map(catName => postsCats.find(cat => cat.catName === catName) ).filter(Boolean); // 过滤掉找不到匹配项的无效值 setSelectedCategories(convertedCategories); } } }, [postsCats, selectedCategories, loading, setSelectedCategories]); // 其余代码保持不变 }
2. 验证逻辑正确性
- 当组件加载完成后,会自动将传入的字符串分类名称转换为包含
catName和id的完整对象 - Autocomplete的
renderTags函数就能正确读取option.catName渲染标签文本 - 提交时的
selectedCategories.map(cat => cat.catName)逻辑保持不变,依然能正确生成后端需要的字符串数组
内容的提问来源于stack exchange,提问作者sultan.h
相关产品推荐
相关产品推荐

