React多复选框筛选异常:多选时仅返回单个结果
React多复选框筛选异常问题修复
问题原因
核心问题在于复选框筛选的useEffect中,直接将过滤后的结果赋值给freelancers状态,导致后续筛选的数据源是已经过滤过的子集,而非原始的完整数据列表。例如:
- 第一次勾选"المغنيين",
freelancers被更新为仅包含该分类的条目 - 再勾选"المصور ومرافق التصوير"时,会从已过滤的"المغنيين"列表中查找该分类,自然无法匹配,最终结果只保留第一次筛选的条目
此外,handleCheckbox的取消逻辑中参数不统一(传入specialization但其实可以直接用event.target.value),不过这不是导致多选异常的主因。
修复方案
- 保持
initialFreelancers为原始数据源,不修改freelancers状态,而是通过计算属性同时整合搜索和复选框的筛选条件 - 移除修改
freelancers的useEffect,避免状态污染 - 统一
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
相关产品推荐
相关产品推荐

