React Table添加筛选条件后选中行隐藏功能失效问题
问题根因
你传内联filter给Table的时候,根本没把hidden状态的过滤逻辑加进去,当然藏不住选中行。
你之前写的matchData之所以能正常隐藏,是因为它专门做了一层过滤:把id存在hidden里的行全部排除了。但你现在写的内联filter只判断了applyStatus和showGood两个条件,完全没处理hidden状态,点按钮后id确实存进hidden了,但渲染数据源的时候没排除这些行,自然看不到隐藏效果。
除此之外你代码里还有3个会导致功能异常的严重问题:
- 你在组件渲染顶层直接写了
if (menus.apply === 0) setApplyStatus(true)这类逻辑:menus是数组,根本没有apply属性,这两个判断永远不会触发;而且在渲染阶段直接调用state setter会触发无限重渲染,直接导致页面报错。 - 你写了个依赖为
menus的useEffect,回调里又调用setMenus修改menus的值,会触发死循环:menus变更→执行effect→修改menus→menus再次变更→再次执行effect,页面直接卡死。 - 你定义
matchData的时候语法错误,多了左括号少了右括号,本身就跑不起来。
修复步骤
- 删掉组件顶层那两个错误的
setApplyStatus判断,applyStatus如果没有特殊的切换需求就不要单独存state,直接把判断逻辑合并到数据过滤里即可;如果需要切换,加对应的控制开关,绝对不能在渲染阶段直接改state。 - 删掉那个会触发死循环的排序useEffect,排序逻辑直接合并到数据处理流程里,不要直接修改原始的menus状态。
- 把所有过滤逻辑(隐藏行判断、apply字段过滤、口味过滤)、排序逻辑用
useMemo合并计算,生成最终的表格数据源,不要写内联filter漏逻辑。 - 修正语法错误,列定义和表格数据分开命名避免混淆。
修复后的核心代码见下方:
export default function MenuDisplay() { const { menuId } = useParams(); const [selected, setSelected] = useState({}); const [hidden, setHidden] = useState({}); const [menus, setMenus] = useState([]) const [showGood, setShowGood] = useState(false); // 移除原有错误的applyStatus状态及顶层修改逻辑,如需切换过滤规则可新增对应开关state // 接口请求逻辑保留 useEffect(() => { axios.post("", { menuId: parseInt(menuId) }) .then(res => setMenus(res.data.menus)) .catch(err => console.log(err)) }, [menuId]); // 按钮状态判断逻辑保留 const buttonMode = Object.values(selected).some(isSelected => isSelected) ? "apply" : "cancel"; // 行选择逻辑保留 const rowSelectHandler = (id) => (checked) => { setSelected(selected => ({ ...selected, [id]: checked })); }; // 按钮点击逻辑保留 const handleClick = () => { if (buttonMode === "apply") { const currentlySelected = {}; Object.entries(selected).forEach(([id, isSelected]) => { if (isSelected) currentlySelected[id] = isSelected; }); setHidden({ ...hidden, ...currentlySelected }); const newSelected = {}; Object.keys(selected).forEach(id => newSelected[id] = false); setSelected(newSelected); } else { const currentlyHidden = {}; Object.entries(hidden).forEach(([id, isHidden]) => { if (isHidden) currentlyHidden[id] = isHidden; }); setSelected({ ...selected, ...currentlyHidden }); const newHidden = {}; Object.keys(hidden).forEach(id => newHidden[id] = false); setHidden(newHidden); } }; // 合并所有过滤、排序逻辑,用useMemo缓存避免重复计算 const tableData = useMemo(() => { // 第一步:过滤已隐藏的行 let result = menus.filter(({ _id }) => !hidden[_id]); // 第二步:过滤apply字段,排除apply=1的行,如需切换规则可在此处加判断 result = result.filter(i => i.apply !== 1); // 第三步:按showGood开关过滤口味 if (!showGood) result = result.filter(i => i.taste !== "Good"); // 第四步:排序,Good口味的行靠前,替换原有死循环的排序逻辑 const goodMenus = result.filter(i => i.taste === "Good"); const restMenus = result.filter(i => i.taste !== "Good"); return [...goodMenus, ...restMenus]; }, [menus, hidden, showGood]); // 行样式逻辑保留 const getRowProps = (row) => ({ style: { backgroundColor: selected[row.values.id] ? "lightgrey" : "white" } }); // 列定义(原代码里命名为data容易和表格数据混淆,改名为columns) const columns = [ { Header: "id", accessor: (row) => row._id }, { Header: "Name", accessor: (row) => <Link to={{ pathname: `/menu/${menuId}/${row._id}` }}>{row.name}</Link> }, { Header: "Description", accessor: (row) => row.description }, { Header: "Dishes", accessor: (row) => row.dishes, id: "dishes", Cell: ({ value }) => value && Object.values(value[0]).join(", ") }, { Header: "Status", accessor: (row) => row.status }, { Header: "Show", accessor: (row) => <Toggle value={selected[row._id]} onChange={rowSelectHandler(row._id)} /> } ]; const initialState = { sortBy: [ { desc: false, id: "id" }, { desc: false, id: "description" } ], hiddenColumns: ["dishes", "id"] }; return ( <div> <button type="button" onClick={handleClick}> {buttonMode === "cancel" ? "Cancel" : "Apply"} </button> show good <Toggle value={showGood} onChange={() => setShowGood(!showGood)} /> <Table data={tableData} columns={columns} initialState={initialState} withCellBorder withRowBorder withSorting withPagination rowProps={getRowProps} /> </div> ); }
如果你需要保留
applyStatus的切换逻辑,只需要新增对应的控制开关,把过滤判断加到useMemo里的对应位置即可,不要在渲染流程中直接修改state。
内容的提问来源于stack exchange,提问作者Zokulko
相关产品推荐
相关产品推荐

