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

React多复选框筛选异常:多选时仅返回单个结果

React多复选框筛选异常问题修复

问题原因

核心问题在于复选框筛选的useEffect中,直接将过滤后的结果赋值给freelancers状态,导致后续筛选的数据源是已经过滤过的子集,而非原始的完整数据列表。例如:

  • 第一次勾选"المغنيين",freelancers被更新为仅包含该分类的条目
  • 再勾选"المصور ومرافق التصوير"时,会从已过滤的"المغنيين"列表中查找该分类,自然无法匹配,最终结果只保留第一次筛选的条目

此外,handleCheckbox的取消逻辑中参数不统一(传入specialization但其实可以直接用event.target.value),不过这不是导致多选异常的主因。

修复方案

  1. 保持initialFreelancers为原始数据源,不修改freelancers状态,而是通过计算属性同时整合搜索和复选框的筛选条件
  2. 移除修改freelancers的useEffect,避免状态污染
  3. 统一handleCheckbox的参数使用,确保逻辑一致性

修正后的关键代码

替换筛选逻辑部分

删除原有的getFilterdFreelancers函数和复选框的useEffect,替换为以下统一的筛选计算:

// 整合搜索与复选框的筛选逻辑
const getFilteredFreelancers = () => {
  if (!initialFreelancers) return null;

  let result = [...initialFreelancers];

  // 复选框筛选
  if (checkboxValues.length > 0) {
    result = result.filter(freelancer => 
      checkboxValues.includes(freelancer.profile.specialization.value)
    );
  }

  // 搜索框筛选
  if (searchInput) {
    result = result.filter(freelancer => 
      freelancer.firstName.toLowerCase().includes(searchInput) || 
      freelancer.lastName.toLowerCase().includes(searchInput)
    );
  }

  return result;
};

const filteredFreelancers = getFilteredFreelancers();

修正handleCheckbox函数

const handleCheckbox = (event) => {
  const value = event.target.value;
  if (event.target.checked) {
    setCheckboxValues(prev => [...prev, value]);
    // 使用函数式更新避免闭包问题
  } else {
    setCheckboxValues(prev => prev.filter(item => item !== value));
  }
};

渲染部分的复选框调用修正

将复选框的onChange改为:

onChange={handleCheckbox}

完整修正后的组件代码

import axios from 'axios';
import React, { useState, useEffect } from 'react';
import { createRoot } from 'react-dom/client';
import Skeleton from 'react-loading-skeleton';
import 'react-loading-skeleton/dist/skeleton.css';


export default function SearchFreelancers(props) {
    const [initialFreelancers, setInitialFreelancers] = useState(null);
    const [specializations, setSpecializations] = useState(null);
    const [searchInput, setSearchInput] = useState("");
    const [checkboxValues, setCheckboxValues] = useState([]);

    useEffect(() => {
        axios.get('api/specializations').then(response => {
            setSpecializations(response.data);
        }).catch(error => {
            console.log(error);
        })
    }, [])

    useEffect(() => {
        axios.get('api/freelnacers').then(response => {
            setInitialFreelancers(response.data);
        }).catch(error => {
            console.log(error);
        })
    }, []);


    const handleChange = value => {
        let lowerCasedValue = value.toLowerCase();
        setSearchInput(lowerCasedValue);
    };

    // 整合搜索与复选框的筛选逻辑
    const getFilteredFreelancers = () => {
        if (!initialFreelancers) return null;

        let result = [...initialFreelancers];

        // 复选框筛选
        if (checkboxValues.length > 0) {
            result = result.filter(freelancer => 
                checkboxValues.includes(freelancer.profile.specialization.value)
            );
        }

        // 搜索框筛选
        if (searchInput) {
            result = result.filter(freelancer => 
                freelancer.firstName.toLowerCase().includes(searchInput) || 
                freelancer.lastName.toLowerCase().includes(searchInput)
            );
        }

        return result;
    };

    const filteredFreelancers = getFilteredFreelancers();


    const handleCheckbox = (event) => {
        const value = event.target.value;
        if (event.target.checked) {
            setCheckboxValues(prev => [...prev, value]);
        } else {
            setCheckboxValues(prev => prev.filter(item => item !== value));
        }
    };


    return (
        <div className="bg-[#F0F0F0]">
            <div className="flex items-center justify-between mb-4 p-4">
                <h1 className="font-smeibold text-xl text-gray-800">ابحث عن المبدع المناسب</h1>
                <select name="" id=""
                    className="block p-2 pl-10 text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500">
                    <option value="">فرز حسب</option>
                </select>
            </div>
            <div className="grid grid-cols-3 gap-4  p-4">
                <div className="mt-8">
                    <div className="relative mt-4">
                        <div className="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
                            <svg aria-hidden="true" className="w-5 h-5 text-gray-500 dark:text-gray-400" fill="none"
                                stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
                                    d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
                            </svg>
                        </div>
                        <input
                            type="text"
                            id="default-search"
                            className="block p-4  w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 "
                            value={searchInput}
                            onChange={e => handleChange(e.target.value)}
                            placeholder="أكتب ماتريد البحث عنه..."
                            required
                        />
                    </div>
                    <div className="mt-3 mr-1">
                        <h1 className="text-xl text-gray-800">المجال</h1>
                        {specializations && specializations.map((specialization, index) => {
                            return (
                                <div key={index} className="flex items-center mb-4 mt-2">
                                    <input
                                        id={specialization.value}
                                        type="checkbox"
                                        value={specialization.value}
                                        onChange={handleCheckbox}
                                        className="w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500" />
                                    <label htmlFor={specialization.value} className="mr-2 text-sm font-medium text-gray-900">
                                        {specialization.label}
                                    </label>
                                </div>
                            )
                        })}
                    </div>
                </div>

                {filteredFreelancers === null || filteredFreelancers === undefined ?
                    <div className='col-span-2 mt-8'>
                        <Skeleton count={30} />
                    </div>
                    :
                    <div className="col-span-2 mt-8">
                        <div className="grid grid-cols-3 gap-4 mt-2">
                            {filteredFreelancers.map((freelnacer, index) => (
                                <div key={index} className=" flex flex-col items-center  bg-white p-4  shadow-sm rounded-xl mt-2 mb-20">
                                    <div className="-mt-16">
                                        <img className="w-28 h-28 shadow-md rounded-xl" src={freelnacer.user_avatar}
                                            alt="image" />
                                    </div>
                                    <div className="flex flex-col items-center justify-between text-center  h-full">
                                        <div className="">
                                            <h2 className="text-md text-center font-bold text-gray-800 mt-2">{freelnacer.user_full_name}</h2>
                                            <p className="text-sm text-gray-600 mt-1">
                                                {freelnacer.profile.specialization.label}
                                            </p>
                                        </div>
                                        <div className=" w-full mt-6 mb-4">
                                            <a href={props.url + `/u/${freelnacer.username}`}
                                                className="bg-white text-[#6F3F70] border border-[#6F3F70] px-8 py-2 rounded-full hover:bg-[#6F3F70] hover:text-white transition-all">
                                                الملف
                                                الشخصي
                                            </a>
                                        </div>
                                    </div>
                                </div>
                            ))}
                        </div>
                    </div>
                }
            </div>
        </div>
    )
}

if (document.getElementById('SearchFreelancers')) {
    const props = Object.assign({}, document.getElementById('SearchFreelancers').dataset)
    createRoot(document.getElementById('SearchFreelancers')).render(<SearchFreelancers {...props} />);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 09:05:21