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

React项目实现Checkbox默认全选并维持计数功能正常的方案

解决方案

核心问题分析

当前代码存在状态分散管理的问题:父组件维护checkedState用于计数,但子组件Card自己维护check状态,导致初始时父组件状态为空、子组件默认未选中,两者状态完全不同步。同时父组件未初始化checkedState为全选状态。

具体修改步骤

1. 父组件:初始化全选状态并统一管理

修改PopupOrcamentoSimultaneo组件,在获取到API数据后,自动初始化符合条件的profissionais为全选状态,同时优化列表生成逻辑:

import React, { useEffect, useState, useMemo } from "react";
import "./popupOrcamentoSimultaneo.css";
import Card from "./Card";
import api from "../../../../api";

function PopupOrcamentoSimultaneo({ setOpenPopup }) {
  const [profissionais, setProfissionais] = useState([]);
  const [value, setValue] = useState(
    "Olá! Vamos nos casar e gostariamos de mais informações sobre seu serviço."
  );
  const [checkedState, setCheckedState] = useState({});

  useEffect(() => {
    api.get("profissionais/listarTodos/").then(({ data }) => {
      setProfissionais(data);
      // 初始化全选状态:筛选符合条件的前4个profissionais并设为选中
      const urlParam = window.location.href.split("=");
      const idProfissionalURL = urlParam.length > 1 ? urlParam[1] : null;
      const found = data.find(obj => obj.idProfissional == idProfissionalURL);
      const targetProfissionais = data.filter(obj => obj.segmento == found?.segmento).slice(0,4);
      
      const initialChecked = {};
      targetProfissionais.forEach(prof => {
        initialChecked[prof.nomeEmpresa] = true;
      });
      setCheckedState(initialChecked);
    });
  }, []);

  const handleOnChange = (e) => {
    setCheckedState(prev => ({ ...prev, [e.target.name]: e.target.checked }));
  };

  // 用useMemo缓存卡片列表,避免重复渲染
  const listaCardsProfissionais = useMemo(() => {
    const urlParam = window.location.href.split("=");
    const idProfissionalURL = urlParam.length > 1 ? urlParam[1] : null;
    const found = profissionais.find(obj => obj.idProfissional == idProfissionalURL);
    const foundSegmento = found?.segmento;

    return profissionais
      .filter(obj => obj.segmento == foundSegmento)
      .slice(0,4)
      .map(prof => (
        <Card
          key={prof.idProfissional}
          dadosProfissionais={prof}
          isChecked={checkedState[prof.nomeEmpresa] || false}
          handleOnChange={handleOnChange}
        />
      ));
  }, [profissionais, checkedState]);

  const changeValue = (e) => {
    setValue(e.target.value);
  };

  // 提取目标estado用于标题
  const targetEstado = useMemo(() => {
    const urlParam = window.location.href.split("=");
    const idProfissionalURL = urlParam.length > 1 ? urlParam[1] : null;
    return profissionais.find(obj => obj.idProfissional == idProfissionalURL)?.estado || "";
  }, [profissionais]);

  return (
    <div className="popup">
      <div className="popup__container">
        <div className="close-container" onClick={() => setOpenPopup(false)}>
          <i className="fa-solid fa-xmark"></i>
        </div>
        <h2 className="popup__titulo">
          {`Profissionais desse segmento em ${targetEstado} recomendados para você.`}
        </h2>

        <div className="cards__profissionais">{listaCardsProfissionais}</div>
        <div className="input-button">
          <div className="input-box">
            <label htmlFor="input" className="input-label">
              Escreva sua mensagem
            </label>
            <div>
              <input
                type="text"
                id="input"
                className="input-text"
                placeholder="Olá! Vamos nos casar e gostaríamos de mais informações sobre seu serviço."
                value={value}
                onChange={changeValue}
              />
              <i className="fa-solid fa-pencil"></i>
            </div>
          </div>
          <button type="submit" className="botao-orcamento">
            Solicitar Orçamento (
            {Object.values(checkedState).filter(Boolean).length})
          </button>
        </div>
      </div>
    </div>
  );
}

export default PopupOrcamentoSimultaneo;

2. 子组件:移除独立状态,依赖父组件传入的状态

修改Card组件,删除自身的check状态,完全由父组件控制选中状态:

import React from "react";
import "./card.css";
import semImagem from "../../../../../fileContents/imagensVitrineProfissional/no-image.png";

function Card(props) {
  const { dadosProfissionais, isChecked, handleOnChange } = props;
  const { nomeEmpresa, cidade, imagemMarketplace } = dadosProfissionais;

  const imagemArquivo = imagemMarketplace ? imagemMarketplace : semImagem;

  return (
    <div className="card__profissional" key={dadosProfissionais.idProfissional}>
      <div className="card__header">
        <img
          className="card-imagem"
          src={imagemArquivo}
          alt={`${nomeEmpresa}`}
        />
      </div>
      <div className="card__body">
        <h2 className="card__titulo">{nomeEmpresa}</h2>
        <span className="tag">{cidade}</span>
        <div className="checkbox">
          <label className="checkbox-label">
            Solicitar Orçamento
            <input
              type="checkbox"
              className="checkbox-input"
              name={nomeEmpresa}
              value={nomeEmpresa}
              checked={isChecked}
              onChange={handleOnChange}
            />
            <span className="checkmark"></span>
          </label>
        </div>
      </div>
    </div>
  );
}

export default Card;

修改说明

  • 状态统一:所有checkbox的选中状态由父组件checkedState集中管理,子组件仅负责展示和触发状态变更,彻底解决状态不一致问题。
  • 初始全选:API数据加载完成后,自动将符合条件的前4个profissionais设为选中,同时计数自动更新为对应数量。
  • 性能优化:用useMemo缓存卡片列表和目标地区信息,避免每次渲染重复执行筛选逻辑;用profissional的唯一ID作为列表key,提升渲染稳定性。
  • 细节修复:将label的for属性改为React规范的htmlFor,移除子组件冗余的onClick事件(onChange已足够处理状态变更)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 08:25:20