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

React TypeScript中Material-UI Chip组件粘贴邮箱覆盖问题求助

问题原因与解决方案

核心问题1:粘贴时直接替换原有邮箱列表

你在handlePaste里用setItem(toBeAdded),这会直接覆盖之前的items数组,而不是把新邮箱追加进去,这就是第二次粘贴丢失原有内容的原因。

核心问题2:回车添加邮箱时直接修改state数组

handleKeyDown里的items.push(test)直接修改了React的state数组,违反了React状态不可变的原则,可能导致状态更新异常。


关键代码修复

  1. 修复粘贴逻辑:
const handlePaste = (evt: React.ClipboardEvent) => {
  evt.preventDefault();

  const paste = evt.clipboardData.getData("text");
  const emails = paste.match(/[\w\d\.-]+@[\w\d\.-]+\.[\w\d\.-]+/g);

  if (emails) {
    const toBeAdded = emails.filter((email: string) => !isInList(email));
    // 合并原有数组与新数组,而非替换
    setItem(prevItems => [...prevItems, ...toBeAdded]);
  }
};
  1. 修复回车添加逻辑:
const handleKeyDown = (evt: React.KeyboardEvent) => {
  if (["Enter", "Tab", "|"].includes(evt.key)) {
    evt.preventDefault();

    const test = value.trim();

    if (test && isValid(test)) {
      // 用函数式更新创建新数组,不直接修改原state
      setItem(prevItems => [...prevItems, test]);
      setValue("");
    }
  }
};

完整修正代码

import Chip from "@material-ui/core/Chip";
import TextField from "@material-ui/core/TextField";
import React, { useRef, useState } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

export const TagActions = () => {
  const [items, setItem] = useState<string[]>([]);
  const [value, setValue] = useState("");
  const [error, setError] = useState("");
  const divRef = useRef<HTMLInputElement>(null);
  const [flag, setFlag] = useState(false);

  const handleDelete = (item: string) => {
    const result = items.filter((i) => i !== item);
    setItem(result);
  };

  const handleItemEdit = (item: string) => {
    const result = items.filter((i) => i !== item);
    setItem(result);
    setValue(item);
  };

  const handleKeyDown = (evt: React.KeyboardEvent) => {
    if (["Enter", "Tab", "|"].includes(evt.key)) {
      evt.preventDefault();

      const test = value.trim();

      if (test && isValid(test)) {
        setItem(prevItems => [...prevItems, test]);
        setValue("");
      }
    }
  };

  const isValid = (email: string) => {
    let errorMsg = null;

    if (isInList(email)) {
      errorMsg = `${email} 已被添加过。`;
    }

    if (!isEmail(email)) {
      setFlag(true);
    }

    if (errorMsg) {
      setError(errorMsg);
      return false;
    }

    return true;
  };

  const isInList = (email: string) => {
    return items.includes(email);
  };

  const isEmail = (email: string) => {
    return /[\w\d\.-]+@[\w\d\.-]+\.[\w\d\.-]+/.test(email);
  };

  const handleChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
    setValue(evt.target.value);
  };

  const handlePaste = (evt: React.ClipboardEvent) => {
    evt.preventDefault();

    const paste = evt.clipboardData.getData("text");
    const emails = paste.match(/[\w\d\.-]+@[\w\d\.-]+\.[\w\d\.-]+/g);

    if (emails) {
      const toBeAdded = emails.filter((email: string) => !isInList(email));
      setItem(prevItems => [...prevItems, ...toBeAdded]);
    }
  };

  return (
    <>
      <div>
        <TextField
          id="outlined-basic"
          variant="outlined"
          InputProps={{
            startAdornment: items.map((item) => (
              <Chip
                className={!isEmail(item) ? "chip-tag-error" : "chip-tag"}
                key={item}
                tabIndex={-1}
                label={item}
                onDelete={() => handleDelete(item)}
                onClick={() => handleItemEdit(item)}
              />
            )),
          }}
          inputRef={divRef}
          value={value}
          placeholder="输入或粘贴邮箱地址后按 Enter..."
          onKeyDown={handleKeyDown}
          onChange={handleChange}
          onPaste={handlePaste}
        />
      </div>

      {error && <p className="error">{error}</p>}
    </>
  );
};

额外优化点:

  • 给事件参数加上了TypeScript正确类型,避免any类型隐患
  • 把TextField的ref改为inputRef,确保指向输入框元素而非外层容器
  • 调整变量命名避免冲突,提升代码可读性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 21:15:41