React中useState与多复选框不同步问题求助
问题描述
我正在构建一个多复选框组件,用useState把选中的值存入数组,但出现状态不同步问题:
- 选中红色选项,数组无新增值
- 选中蓝色选项,数组仅加入之前选中的红色值
- 选中绿色选项,数组仅包含红色和蓝色值
组件代码
import React, { useEffect, useState } from "react"; import { connect } from "react-redux"; import { filterProducts } from "../../redux/Shopping/shopping-actions"; const filterValues = [ { heading: "Colour", items: ["Red", "Blue", "Green"], }, { heading: "Gender", items: ["Men", "Women"], }, { heading: "Price", items: ["0-Rs250", "Rs251-450", "Rs 450"], }, { heading: "Type", items: ["Polo", "Hoodie", "Classic"], }, ]; const Filter = ({ filter, searchArray, setSearchArray }) => { const handleCheck = (e) => { if (e.target.checked === true) { setSearchArray([...searchArray, e.target.value]); } else { setSearchArray(searchArray.filter((id) => id !== e.target.value)); } if (searchArray.length > 0) { filter(searchArray); } }; return ( <div> <aside className="ml-4 mt-16"> {filterValues.map((item) => { return ( <> <h3 className="mb-4 mt-2 ml-4 font-semibold text-gray-900 dark:text-black"> {item.heading} </h3> <ul className="w-48 text-sm font-medium text-black-900 bg-white dark:text-white"> {item.items.map((i) => { return ( <li className="w-full rounded-t-lg"> <div className="flex items-center pl-3"> <input id="checkbox" type="checkbox" value={i} onChange={(e) => handleCheck(e)} className="w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500" /> <label for="checkbox" className="py-3 ml-2 w-full text-sm font-medium text-gray-900 dark:text-black" > {i} </label> </div> </li> ); })} </ul> </> ); })} </aside> </div> ); }; const mapDispatchToProps = (dispatch) => { return { filter: (query) => dispatch(filterProducts(query)), }; }; export default connect(null, mapDispatchToProps)(Filter);
Redux筛选Reducer代码
case actionTypes.FILTER_PRODUCT: const searchParameters = data && Object.keys(Object.assign({}, ...data)); let filterData; console.log("testing", action.payload); if (action.payload.length === 0) { filterData = state.products; } else { action.payload.forEach((i) => { filterData = state.products.filter((item) => searchParameters.some((parameter) => item[parameter] .toString() .toLowerCase() .includes(i.toLowerCase()) ) ); }); } return { ...state, filteredData: filterData, }; default: return state; }
问题根源
- useState异步更新特性:
setSearchArray是异步操作,调用后searchArray不会立刻刷新,所以紧接着调用filter(searchArray)时,用的还是更新前的旧数组。 - 复选框ID重复:所有复选框的
id都是checkbox,点击标签时可能触发错误的复选框,属于潜在bug。 - Reducer筛选逻辑错误:
forEach循环每次都会覆盖filterData,最终只保留最后一个筛选条件的结果,无法实现多条件叠加筛选。
解决方案
1. 修复useState异步问题
用函数式更新获取最新状态,或者通过useEffect监听状态变化触发筛选:
方案一:函数式更新内触发筛选
const handleCheck = (e) => { const value = e.target.value; setSearchArray(prev => { const newArray = e.target.checked ? [...prev, value] : prev.filter(id => id !== value); // 用最新的数组调用筛选 filter(newArray); return newArray; }); };
方案二:useEffect监听状态变化
useEffect(() => { filter(searchArray); }, [searchArray, filter]); const handleCheck = (e) => { const value = e.target.value; if (e.target.checked) { setSearchArray(prev => [...prev, value]); } else { setSearchArray(prev => prev.filter(id => id !== value)); } };
2. 修复复选框ID重复问题
给每个复选框生成唯一ID,同时把JSX中的for属性改为htmlFor:
{item.items.map((i) => { const uniqueId = `checkbox-${item.heading}-${i}`; return ( <li className="w-full rounded-t-lg" key={uniqueId}> <div className="flex items-center pl-3"> <input id={uniqueId} type="checkbox" value={i} onChange={(e) => handleCheck(e)} // 其他属性不变 /> <label htmlFor={uniqueId} // 其他属性不变 > {i} </label> </div> </li> ); })}
3. 修复Reducer多条件筛选逻辑
用reduce替代forEach,实现多条件叠加筛选:
case actionTypes.FILTER_PRODUCT: const searchParameters = data && Object.keys(Object.assign({}, ...data)); let filterData; if (action.payload.length === 0) { filterData = state.products; } else { // 逐步叠加所有筛选条件 filterData = action.payload.reduce((acc, filterValue) => { return acc.filter(item => searchParameters.some(parameter => item[parameter] .toString() .toLowerCase() .includes(filterValue.toLowerCase()) ) ); }, state.products); } return { ...state, filteredData: filterData, };
完整修改后的组件代码
import React, { useEffect, useState } from "react"; import { connect } from "react-redux"; import { filterProducts } from "../../redux/Shopping/shopping-actions"; const filterValues = [ { heading: "Colour", items: ["Red", "Blue", "Green"], }, { heading: "Gender", items: ["Men", "Women"], }, { heading: "Price", items: ["0-Rs250", "Rs251-450", "Rs 450"], }, { heading: "Type", items: ["Polo", "Hoodie", "Classic"], }, ]; const Filter = ({ filter, searchArray, setSearchArray }) => { useEffect(() => { filter(searchArray); }, [searchArray, filter]); const handleCheck = (e) => { const value = e.target.value; if (e.target.checked) { setSearchArray(prev => [...prev, value]); } else { setSearchArray(prev => prev.filter(id => id !== value)); } }; return ( <div> <aside className="ml-4 mt-16"> {filterValues.map((item) => { return ( <> <h3 className="mb-4 mt-2 ml-4 font-semibold text-gray-900 dark:text-black"> {item.heading} </h3> <ul className="w-48 text-sm font-medium text-black-900 bg-white dark:text-white"> {item.items.map((i) => { const uniqueId = `checkbox-${item.heading}-${i}`; return ( <li className="w-full rounded-t-lg" key={uniqueId}> <div className="flex items-center pl-3"> <input id={uniqueId} type="checkbox" value={i} onChange={(e) => handleCheck(e)} className="w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-700 focus:ring-2 dark:bg-gray-600 dark:border-gray-500" /> <label htmlFor={uniqueId} className="py-3 ml-2 w-full text-sm font-medium text-gray-900 dark:text-black" > {i} </label> </div> </li> ); })} </ul> </> ); })} </aside> </div> ); }; const mapDispatchToProps = (dispatch) => { return { filter: (query) => dispatch(filterProducts(query)), }; }; export default connect(null, mapDispatchToProps)(Filter);
内容的提问来源于stack exchange,提问作者gemie
相关产品推荐
相关产品推荐

