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

React中useState验证函数取旧状态值致NFC标签重复验证问题

问题原因分析

你遇到的问题核心是闭包捕获了旧的State引用,再加上直接修改原State数组的操作,导致状态判断逻辑失效:

  • 调用scanTag后,ndef.onreading回调会捕获当时的tags状态值。后续删除标签触发组件重新渲染时,新的tags状态生成,但之前注册的onreading回调仍然引用删除前的旧tags数组,所以验证逻辑会误判标签已存在。
  • 添加标签时const list = tags; list.push(tag);直接修改原State数组,React无法检测到数组的引用变化,可能导致状态更新不及时。

修复方案

1. 使用函数式更新获取最新State

通过setTags的函数式参数,直接在回调里获取最新的prevTags状态,避免闭包带来的旧值问题,同时创建新数组而非修改原数组:

setTags(prevTags => {
  // 在最新状态里检查标签是否已存在
  if (prevTags.some(tag => tag.id === serialNumber)) {
    alert("already registered");
    return prevTags;
  }
  // 返回新数组添加标签
  return [...prevTags, { id: serialNumber }];
});

2. 优化扫描监听逻辑

每次扫描后移除监听,避免多次调用scanTag后触发重复回调:

async function scanTag() {
  const ndef = new NDEFReader();
  await ndef.scan();

  const handleReading = ({ serialNumber }) => {
    setTags(prevTags => {
      if (prevTags.some(tag => tag.id === serialNumber)) {
        alert("already registered");
        return prevTags;
      }
      return [...prevTags, { id: serialNumber }];
    });
    // 扫描到一次后停止监听
    ndef.removeEventListener("reading", handleReading);
  };

  ndef.addEventListener("reading", handleReading);
}

3. 删除逻辑保持不变

你的deleteTag函数已经正确使用filter返回新数组,React能正常检测到状态变化,无需修改。


完整修复代码
import { useState } from "react";

const App = () => {
  const [tags, setTags] = useState([]);

  async function scanTag() {
    const ndef = new NDEFReader();
    await ndef.scan();

    const handleReading = ({ serialNumber }) => {
      setTags(prevTags => {
        if (prevTags.some(tag => tag.id === serialNumber)) {
          alert("already registered");
          return prevTags;
        }
        return [...prevTags, { id: serialNumber }];
      });
      ndef.removeEventListener("reading", handleReading);
    };

    ndef.addEventListener("reading", handleReading);
  }

  function deleteTag(index) {
    setTags(tags.filter((_, i) => i !== index));
  }

  return (
    <div>
      {tags.map((tag, index) => (
        <div key={tag.id}>
          <span>{tag.id}</span>
          <button onClick={() => deleteTag(index)}>Delete</button>
        </div>
      ))}
      <button onClick={scanTag}>Scan Tag</button>
    </div>
  );
};

export default App;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 22:12:44