You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从数据库恢复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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 22:07:37