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

React中Material UI Autocomplete新增分类后切换焦点内容消失问题

解决Material UI Autocomplete焦点切换后内容消失问题

问题描述

我在React应用中使用Material UI的Autocomplete组件实现分类的添加与删除,添加分类通过Dialog弹窗完成。目前点击Dialog确认按钮后,分类能成功存入数据库,且分类名称会显示在Autocomplete输入框中,但按下Tab键切换到表单下一个输入框时,Autocomplete输入框中的内容会消失,需要解决这个焦点切换后内容保留的问题。

问题原因分析

  1. 错误的状态更新逻辑:在handleAddCategory函数中,先设置autocompleteInput为新分类名称,随后又错误地将其设置为newCategoryName.value(此时newCategoryName已被清空),导致输入框内容被重置。
  2. 不严谨的useEffect监听:每次categories数组更新(包括删除分类操作)都会触发useEffect,强制将autocompleteInput设置为最后一个分类名称,覆盖用户输入。
  3. 未绑定Autocomplete的value属性:仅绑定inputValue会导致组件无法正确识别选中的选项,焦点切换时可能触发输入框清空。

解决方案

  • 修复handleAddCategory中的状态更新逻辑,移除错误的状态赋值。
  • 添加状态标记区分分类添加操作,优化useEffect的触发条件。
  • 为Autocomplete绑定value属性,同步选中的选项状态。

修改后的完整代码

function EnterForm() {
  const [input, setInput] = useState({
    amount: "",
    category: "",
    comment: "",
    date: "",
  });

  const [categories, setCategories] = useState([]);
  const [openDialog, setOpenDialog] = useState(false);
  const [newCategoryName, setNewCategoryName] = useState("");
  const [autocompleteInput, setAutocompleteInput] = useState("");
  const [alertOpen, setAlertOpen] = useState(false);
  const [isAddingCategory, setIsAddingCategory] = useState(false); // 标记是否正在添加分类

  const getTransactions = async () => {
    await axios
      .get("http://localhost:3001/read")
      .then((res) => setCategories(res.data))
      .catch((err) => console.log(err));
  };

  const getCategory = async () => {
    await axios.get("http://localhost:3001/readCategory")
      .then((res) => setCategories(res.data))
      .catch((err) => console.log(err));
  }

  useEffect(() => {
    getTransactions();
    getCategory();
  }, []);

  useEffect(() => {
    // 仅在添加分类时自动填充输入框
    if (categories.length > 0 && isAddingCategory) {
      const lastCategory = categories[categories.length - 1];
      setAutocompleteInput(lastCategory.autocomplete);
      setInput((prevInput) => ({
        ...prevInput,
        category: lastCategory.autocomplete,
      }));
      setIsAddingCategory(false); // 重置标记
    }
  }, [categories, isAddingCategory]);

  const handleOpenDialog = () => {
    setOpenDialog(true);
    setNewCategoryName(autocompleteInput);
  };

  const handleCloseDialog = () => {
    setOpenDialog(false);
  };

  const handleAddCategory = async (event) => {
    event.preventDefault();
    const categoryToAdd = { autocomplete: newCategoryName };
    try {
      const response = await axios.post("http://localhost:3001/newcategory", categoryToAdd);
      const newCategory = response.data;
      setCategories((prevCategories) => [...prevCategories, newCategory]);
      setNewCategoryName("");
      // 正确设置输入框值
      setAutocompleteInput(newCategory.autocomplete);
      handleCloseDialog();
      setIsAddingCategory(true); // 设置添加标记
      await getCategory();
    } catch (err) {
      console.log(err);
    }
  };

  const handleClose = (event, reason) => {
    if (reason === 'clickaway') {
      return;
    }
    setAlertOpen(false);
  };

  // 找到当前选中的分类选项
  const selectedCategory = categories.find(
    cat => cat.autocomplete === input.category
  );

  return (
    <Grid container sx={{ margin: "5%" }} spacing={2} alignItems="center">
      <Grid item xs={12}>
        <Typography variant="h4" sx={{ margin: 1 }}>
          Introdu tranzactie:
        </Typography>
      </Grid>

      <Grid item xs={12}>
        <div style={{ width: "50%", margin: 5, textAlign: "left" }}>
          <Autocomplete
            fullWidth
            getOptionLabel={(option) => option.autocomplete}
            options={categories}
            inputValue={autocompleteInput}
            value={selectedCategory} // 绑定选中的选项值
            onInputChange={(event, newInputValue) => setAutocompleteInput(newInputValue)}
            onChange={(event, value) => {
              setInput({ ...input, category: value ? value.autocomplete : "" });
              // 同步inputValue
              setAutocompleteInput(value ? value.autocomplete : "");
            }}
            isOptionEqualToValue={(option, value) => option._id === value._id}
            noOptionsText={
              <Button onClick={handleOpenDialog}>
                Add new category
              </Button>
            }
            renderInput={(params) => <TextField {...params} label="Categorie" />}
            renderOption={(props, option) => (
              <Box component="li" {...props} key={option._id}>
                {option.autocomplete}
                <IconButton
                  style={{ color: "#c70000" }}
                  sx={{ marginLeft: "auto" }}
                  aria-label="delete"
                  onClick={(event) => {
                    event.stopPropagation();
                    handleDeleteCategory(option._id);
                  }}
                >
                  <DeleteIcon />
                </IconButton>
              </Box>
            )}
          />
        </div>
      </Grid>

      <Dialog open={openDialog} onClose={handleCloseDialog}>
        <DialogTitle>Add a New Category</DialogTitle>
        <DialogContent>
          <DialogContentText>
            To add a new category, please enter the category name here.
          </DialogContentText>
          <TextField
            autoFocus
            margin="dense"
            id="name"
            label="Category Name"
            type="text"
            fullWidth
            variant="standard"
            value={newCategoryName}
            onChange={(e) => setNewCategoryName(e.target.value)}
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={handleCloseDialog}>Cancel</Button>
          <Button onClick={handleAddCategory}>Add</Button>
        </DialogActions>
      </Dialog>
    </Grid>
  );
}

export default EnterForm;

内容的提问来源于stack exchange,提问作者Victor Arodu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 12:44:57