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

React组件渲染两次(useEffect执行两次)及相关问题咨询

问题解决:React组件渲染两次、count显示异常及事件处理器错误

1. Strict Mode下组件/useEffect执行两次

React Strict Mode在开发环境下会刻意重复调用组件渲染和副作用函数(比如useEffect),目的是帮你排查潜在的副作用问题(比如非幂等操作),生产环境不会有这个行为。不需要移除Strict Mode,你的useEffect里已经通过判断商品是否存在避免了重复添加,逻辑是安全的,这个现象属于正常机制。

2. count显示为01而非1

问题出在count的初始值和更新方式:

  • 你把count初始值设成了数组[0],执行setCount(count + 1)时,数组和数字相加会被隐式转成字符串拼接,得到"01"。
  • 直接用count + 1还会有闭包问题,useEffect依赖的是prop.index,更新时拿到的count可能是旧值。

修正:

// 初始值改成数字0
const [count, setCount] = useState(0)

// useEffect里用函数式更新获取最新值
setCount(prevCount => prevCount + 1)

3. 事件处理器报错且未点击就执行

未点击就触发函数

你直接写onClick={handlePlusMinus("-",prop.index)}会导致组件渲染时就调用函数,必须用箭头函数包裹,让点击时才执行:

<button onClick={() => handlePlusMinus("-", prop.index)}>-</button>
<button onClick={() => handlePlusMinus("+", prop.index)}>+</button>

newarray[id][1]未定义报错

prop.index是商品在allItems里的索引,但productstobuy是按添加顺序存储的,两者索引不匹配,用prop.index去取productstobuy里的元素自然找不到。另外你减号逻辑写错了,应该是减1不是加1。

修正后的事件处理器(用商品本身作为标识查找,更可靠):

const handlePlusMinus = (sign, targetProduct) => {
  setProducts(prev => {
    return prev.map(product => {
      if (product[0] === targetProduct) {
        let newCount = product[1]
        if (sign === "+") {
          newCount += 1
        } else if (newCount - 1 >= 1) {
          newCount -= 1
        }
        return [product[0], newCount]
      }
      return product
    })
  })
}

调用时传入当前商品:

{productstobuy.map((product) => {
  return (
    <div className="product" key={product[0]}>
      {product[0]}
      <div>
        <button onClick={() => handlePlusMinus("-", product[0])}>-</button>
        <input type="number" value={product[1]} readOnly />
        <button onClick={() => handlePlusMinus("+", product[0])}>+</button>
      </div>
    </div>
  )
})}

完整修正代码

import React, { useEffect, useState } from "react";
import { useOutletContext } from "react-router-dom";

function ShoppingCart(prop) {
  const [productstobuy, setProducts] = useState([]);
  const [count, setCount] = useState(0)

  const handlePlusMinus = (sign, targetProduct) => {
    setProducts(prev => {
      return prev.map(product => {
        if (product[0] === targetProduct) {
          let newCount = product[1]
          if (sign === "+") {
            newCount += 1
          } else if (newCount - 1 >= 1) {
            newCount -= 1
          }
          return [product[0], newCount]
        }
        return product
      })
    })
  }

  const allItems = useOutletContext()

  useEffect(() => {
    setCount(prevCount => prevCount + 1)
    const toadd = allItems[prop.index]
    const productExists = productstobuy.some(elem => elem[0] === toadd)

    if (!productExists) {
      setProducts(oldArray => [...oldArray, [toadd, 1]]);
    }
  }, [prop.index, allItems])

  return (
    <div>
      {count}
      {productstobuy.length > 0 ? productstobuy.map((product) => {
        return (
          <div className="product" key={product[0]}>
            {product[0]}
            <div>
              <button onClick={() => handlePlusMinus("-", product[0])}>-</button>
              <input type="number" value={product[1]} readOnly />
              <button onClick={() => handlePlusMinus("+", product[0])}>+</button>
            </div>
          </div>
        )
      }) : null}
    </div>
  );
}

export { ShoppingCart }

额外优化:

  • 用allItems[prop.index]代替filter,更高效
  • 用some判断商品是否存在,比filter更简洁
  • 给map元素加key,符合React规范
  • 把input设为只读,避免手动输入导致状态不一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 07:54:22