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

React中从输入框向有序列表添加项时,重新加载后首个列表项重复添加的原因排查

问题原因及修复方案

看起来你遇到的重复添加问题主要有两个核心原因,结合React的渲染机制和你的代码写法导致的,咱们一步步拆解:

1. 错误使用普通变量维护状态,而非React State

你在组件内部声明了const listItems = [],这是一个普通的组件内变量,不是React的状态。每次组件重新渲染时(包括React严格模式下的双重渲染,这在开发环境默认开启),这个变量都会被重新初始化为空数组。而你的clearAddUpdateState函数里,先向这个数组push值,再用它来更新state:

listItems.push(inputRef.current.value)
setItems(prevList => prevList.concat(listItems))

在开发环境的严格模式中,React会故意执行两次组件渲染和更新逻辑来检测副作用,这就导致第一次点击"Add"时,listItems.push被执行了两次,最终concat的时候就把两个相同的值添加到了状态里,出现重复项。

另外,普通变量不会触发组件重新渲染,也不受React状态管理的控制,完全不应该用来维护需要渲染的列表数据。

2. 不稳定的key值(次要但需修复)

你用Math.random()作为<li>的key,这会导致每次组件渲染时,列表项的key都发生变化,React无法正确跟踪列表项的身份,可能引发不必要的重新渲染,甚至在某些场景下导致渲染错误,虽然这不是当前重复问题的直接原因,但也是需要修正的问题。


修复后的代码

我们完全用React的useState来管理列表状态,移除多余的普通变量,同时改用稳定的key:

import { useRef, useState } from 'react'

export function GroceryAllin() {
  const inputRef = useRef(null)
  // 直接初始化空数组作为初始状态,不需要额外的listItems变量
  const [items, setItems] = useState([])

  function removeLastItem() {
    setItems(prevList => prevList.slice(0, prevList.length - 1))
  }

  function clearAddUpdateState() {
    const newItem = inputRef.current.value.trim()
    // 先判断输入是否为空,避免添加空项
    if (!newItem) return
    
    // 直接基于之前的状态更新,不需要依赖外部数组
    setItems(prevList => [...prevList, newItem])
    
    inputRef.current.value = ""
  }

  return (
    <div className="container">
      <label htmlFor="itemInput">Add a/an : </label>
      <input ref={inputRef} type="text" name="itemInput" id="itemInput" />
      <button onClick={clearAddUpdateState} type="button">Add</button>
      {/* 用索引作为key(如果列表项不会被重新排序的话可以用,更好的方式是给每个项生成唯一id) */}
      <ol>{items.map((el, index) => <li key={index}>{el}</li>)}</ol>
      <button onClick={removeLastItem} type="button">Remove last </button>
    </div>
  )
}

关键修改点说明:

  • 移除了listItems普通变量,直接用useState([])初始化列表状态,所有状态更新都通过setItems的回调函数,基于之前的状态(prevList)来更新,确保每次状态更新都是可靠的。
  • 添加了输入内容的空值判断,避免添加空的列表项。
  • 将prevList.concat(listItems)改为[...prevList, newItem],直接把新项添加到之前的列表里,逻辑更清晰,也不需要依赖外部数组。
  • 改用列表项的索引作为key(如果你的列表涉及排序、删除中间项等操作,建议用更稳定的唯一id,比如给每个新项生成一个时间戳:key={Date.now() + index},或者使用uuid库生成唯一标识)。

这样修改后,就不会出现第一次添加项重复的问题了,同时代码也更符合React的状态管理规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 18:17:43