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
相关产品推荐
相关产品推荐

