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

React.js实现Checkbox全选功能及选中商品总价计算

React购物车复选框全选功能实现方案

现有代码问题修复

先修正原有逻辑里的几个影响功能的bug:

  • 单行复选框错误使用value属性绑定选中状态,应改为checked属性
  • 全选复选框事件、属性名不符合原生input规范,应使用onChange绑定事件、checked绑定选中状态
  • 状态更新直接修改原数组不符合React不可变数据原则,统一改为函数式更新+filter/扩展运算符实现
  • getCheckedStatus判断逻辑错误,原逻辑found !== null在找不到元素(返回undefined)时会误判为选中,改为用some直接判断存在性

核心实现逻辑

1. 提取可勾选商品列表

用useMemo缓存所有库存>0的非售罄商品,避免每次渲染重复遍历计算:

const selectableItems = useMemo(() => {
  const items = [];
  cartData.Result.forEach(shop => {
    shop.ShopCartList.forEach(cart => {
      cart.productCartList.forEach(product => {
        if (product.Stock > 0) items.push(product);
      });
    });
  });
  return items;
}, [cartData]);

2. 双向同步全选状态

新增副作用监听选中项、可选项变化,自动更新全选框状态:当所有非售罄商品都被选中时,全选框自动置为选中,任意一项取消选中则全选框同步取消:

useEffect(() => {
  const isAllSelected = selectableItems.every(item => 
    checkedItems.some(checked => 
      checked.ProductID === item.ProductID && checked.VariationID === item.VariationID
    )
  );
  setIsCheckAll(isAllSelected);
}, [checkedItems, selectableItems]);

3. 实现全选/取消全选方法

点击全选框时,若为选中操作则把所有可勾选商品加入选中列表,若为取消操作则清空所有选中项:

const handleSelectAll = (e) => {
  const isChecked = e.target.checked;
  if (isChecked) {
    // 全选:组装所有可选项的选中数据
    const allCheckedItems = selectableItems.map(item => ({
      ProductID: item.ProductID,
      VariationID: item.VariationID,
      Count: item.Count
    }));
    setCheckedItems(allCheckedItems);
  } else {
    // 取消全选:清空选中列表
    setCheckedItems([]);
  }
};

4. 实时计算已选商品总价

用useMemo缓存总价,选中项变化时自动重新计算:

const totalPrice = useMemo(() => {
  return checkedItems.reduce((sum, checkedItem) => {
    // 匹配对应商品获取单价,Price字段可根据实际接口返回字段替换
    let targetProduct = null;
    cartData.Result.forEach(shop => {
      shop.ShopCartList.forEach(cart => {
        const found = cart.productCartList.find(p => 
          p.ProductID === checkedItem.ProductID && p.VariationID === checkedItem.VariationID
        );
        if (found) targetProduct = found;
      });
    });
    return targetProduct ? sum + targetProduct.Price * checkedItem.Count : sum;
  }, 0);
}, [checkedItems, cartData]);

修正后完整代码

import "./styles.css";
import React, { useEffect, useState, useMemo } from "react";

// 此处替换为实际购物车数据
const cartData = { Result: [] };

export default function App() {
  const [isCheckAll, setIsCheckAll] = useState(false);
  const [checkedItems, setCheckedItems] = useState(
    JSON.parse(localStorage.getItem("checkedItems") || "[]")
  );

  // 选中状态持久化
  useEffect(() => {
    localStorage.setItem("checkedItems", JSON.stringify(checkedItems));
  }, [checkedItems]);

  // 提取所有可勾选(非售罄)商品
  const selectableItems = useMemo(() => {
    const items = [];
    cartData.Result.forEach(shop => {
      shop.ShopCartList.forEach(cart => {
        cart.productCartList.forEach(product => {
          if (product.Stock > 0) items.push(product);
        });
      });
    });
    return items;
  }, [cartData]);

  // 自动同步全选状态
  useEffect(() => {
    const isAllSelected = selectableItems.every(item => 
      checkedItems.some(checked => 
        checked.ProductID === item.ProductID && checked.VariationID === item.VariationID
      )
    );
    setIsCheckAll(isAllSelected);
  }, [checkedItems, selectableItems]);

  // 计算已选总价
  const totalPrice = useMemo(() => {
    return checkedItems.reduce((sum, checkedItem) => {
      let targetProduct = null;
      cartData.Result.forEach(shop => {
        shop.ShopCartList.forEach(cart => {
          const found = cart.productCartList.find(p => 
            p.ProductID === checkedItem.ProductID && p.VariationID === checkedItem.VariationID
          );
          if (found) targetProduct = found;
        });
      });
      return targetProduct ? sum + targetProduct.Price * checkedItem.Count : sum;
    }, 0);
  }, [checkedItems, cartData]);

  // 添加选中项
  const addChecked = (itemId, variationId, qty) => {
    setCheckedItems(prev => [
      ...prev,
      { ProductID: itemId, VariationID: variationId, Count: qty }
    ]);
  };

  // 移除选中项
  const removeChecked = (itemId, variationId) => {
    setCheckedItems(prev => prev.filter(
      item => !(item.ProductID === itemId && item.VariationID === variationId)
    ));
  };

  // 获取单行选中状态
  const getCheckedStatus = (itemId, variationId) => {
    return checkedItems.some(
      (item) => item.ProductID === itemId && item.VariationID === variationId
    );
  };

  // 全选/取消全选处理
  const handleSelectAll = (e) => {
    const isChecked = e.target.checked;
    if (isChecked) {
      const allCheckedItems = selectableItems.map(item => ({
        ProductID: item.ProductID,
        VariationID: item.VariationID,
        Count: item.Count
      }));
      setCheckedItems(allCheckedItems);
    } else {
      setCheckedItems([]);
    }
  };

  return (
    <div className="App">
      {cartData.Result.map((shop) =>
        shop.ShopCartList.map((cart) => (
          <div key={cart.ShopID} md="12" lg="12">
            {cart.productCartList.map((items) => {
              return (
                <div key={items.VariationID} md="12" lg="12">
                  <div id="additem" className="pt-5">
                    {items.Stock === 0 ? (
                      <h6 className="bg-light text-danger font-weight-bold ">
                        SOLD OUT
                      </h6>
                    ) : (
                      <input
                        type="checkbox"
                        checked={getCheckedStatus(
                          items.ProductID,
                          items.VariationID
                        )}
                        onChange={(e) => {
                          if (e.target.checked) {
                            addChecked(
                              items.ProductID,
                              items.VariationID,
                              items.Count
                            );
                          } else {
                            removeChecked(
                              items.ProductID,
                              items.VariationID
                            );
                          }
                        }}
                      />
                    )}
                    {/* 商品其他信息展示 */}
                  </div>
                </div>
              );
            })}
          </div>
        ))
      )}
      <div>
        <input
          type="checkbox"
          name="selectAll"
          id="selectAll"
          onChange={handleSelectAll}
          checked={isCheckAll}
        />
        Select All
      </div>
      {/* 总价展示 */}
      <div className="total-price">
        已选商品总价:<em>¥{totalPrice.toFixed(2)}</em>
      </div>
    </div>
  );
}

注意事项

  • 代码中cartData为占位变量,需替换为实际接口返回的购物车数据
  • 总价计算中使用的Price字段为通用命名,需根据实际接口返回的商品单价字段名替换
  • 售罄商品(Stock=0)会自动排除在全选范围外,不参与总价计算

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 13:15:40