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

如何使用React Hooks为列表中每个元素实现独立增减计数器

解决React列表元素计数器独立增减的问题

你遇到的核心问题是所有列表项共享了同一个状态变量——num和disble(注意这里有拼写错误,应该是disable),所以点击任何一个按钮,所有元素的计数器都会同步变化。要让每个元素的计数器独立工作,我们需要给每个列表项单独维护状态。

修正后的完整代码

import { useState } from 'react';

function SyndicateList() {
  const syndicateList = [ 
    { _id : 1, price: 500, shares: 130, lines: 1000 }, 
    { _id: 2, price: 700, shares: 140, lines: 650 } 
  ];

  // 用对象存储每个item的计数器值,键为item的_id
  const [counts, setCounts] = useState(() => {
    return syndicateList.reduce((acc, item) => {
      acc[item._id] = 1;
      return acc;
    }, {});
  });

  // 用对象存储每个item的减号按钮禁用状态,键为item的_id
  const [disables, setDisables] = useState(() => {
    return syndicateList.reduce((acc, item) => {
      // 初始计数器为1,所以减号按钮默认禁用
      acc[item._id] = true;
      return acc;
    }, {});
  });

  const incrementCount = (id) => {
    // 函数式更新状态,确保拿到最新的prev状态
    setCounts(prev => ({
      ...prev,
      [id]: prev[id] + 1
    }));
    // 递增后,减号按钮可以点击,解除禁用
    setDisables(prev => ({
      ...prev,
      [id]: false
    }));
  };

  const decrementCount = (id) => {
    setCounts(prev => {
      // 确保计数器不小于1
      const updatedCount = Math.max(prev[id] - 1, 1);
      // 根据更新后的计数器值,设置减号按钮的禁用状态
      setDisables(disPrev => ({
        ...disPrev,
        [id]: updatedCount === 1
      }));
      return {
        ...prev,
        [id]: updatedCount
      };
    });
  };

  return (
    <>
      {syndicateList.map((syndicate) => (
        <div key={syndicate._id}> {/* 建议用item的_id作为key,比索引更可靠 */}
          <div>价格: {syndicate.price}</div>
          <button 
            className="btn btn-danger px-3" 
            disabled={disables[syndicate._id]} 
            onClick={() => decrementCount(syndicate._id)} 
            type="button"
          >
            -
          </button>
          <span>{counts[syndicate._id]}</span>
          <button 
            className="btn btn-danger px-3" 
            onClick={() => incrementCount(syndicate._id)} 
            type="button"
          >
            +
          </button>
        </div>
      ))}
    </>
  );
}

export default SyndicateList;

关键改动说明

  1. 状态结构调整:

    • 把单个的num改成对象counts,每个键对应列表项的_id,值为该元素的计数器数值,这样每个元素有独立的计数。
    • 把单个的disble改成对象disables,同样用_id作为键,存储对应元素减号按钮的禁用状态。
  2. 函数式状态更新:

    • 在更新状态时使用函数式更新(setCounts(prev => ...)),确保每次更新都基于最新的状态值,避免闭包导致的状态不一致问题。
  3. 键的优化:

    • 渲染列表时用syndicate._id作为key,比索引i更可靠,尤其是当列表项顺序可能变化时。
  4. 修复拼写错误:

    • 修正了disble为disable,避免不必要的bug。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 07:50:00