React基于选中分类的食谱列表过滤功能实现问询
食谱多选分类过滤问题解决方案
问题场景
你在React项目中实现食谱分类过滤:通过recipesCategories循环渲染RecipeCategory组件作为分类按钮,初始所有分类均为选中状态,需求是选中的分类需要将所有标签匹配该分类的食谱加入展示列表,但当前子组件的ChangeRecipeList函数无法正确实现多选逻辑。
你的原始代码:
const recipes = [ { name: 'Chicken-Curry-with-White-Rice', time: '35', tags: ['Indian'] }, { name: 'Herby-Fish-Rolls', time: '35', tags: ['SeaFood'] }, { name: 'Chicken-Shawarma-Tacos', time: '15', tags: ['Chicken', 'Quick & Easy'] }, { name: 'Chicken-Zurbian', time: '55', tags: ['Chicken'] }, { name: 'Shrimp-Scampi', time: '10', tags: ['SeaFood', 'Quick & Easy'] }, { name: 'Baked-Fish-Fillet-in-Parchment-Paper', time: '40', tags: ['SeaFood'] }, { name: 'Teriyaki-Chicken-with-Broccoli', time: '25', tags: ['Asian', 'Chicken'] }, { name: 'Thai-Style-Chicken', time: '30', tags: ['Chicken'] }, { name: 'Chicken-Shawarma-Salad', time: '10', tags: ['Chicken'] }, { name: 'Bulgur-Salad-with-Grilled-Chicken', time: '25', tags: ['Chicken'] }, ]; const recipesCategories = ['Asian', 'Indian', 'SeaFood', 'Quick & Easy', 'Chicken']; const [filteredRecipes, setFilteredRecipes] = useState(recipes); // 渲染分类按钮 {recipesCategories.map((recipe) => { return ( <RecipeCategory recipeCategory={recipeCategory} filteredRecipes={filteredRecipes} setFilteredRecipes={setFilteredRecipes} /> ) })} // 渲染过滤后的食谱 {filteredRecipes.map((recipe) => ( <> <RecipeSlide recipe={recipe} /> </> ))}
当前子组件的错误逻辑:
const ChangeRecipeList = () => { let list = recipes; let newFilteredList = []; console.log('Recipe Catgory:', recipeCategory) newFilteredList = list.filter((Recipe) => selected && Recipe.tags.includes(recipeCategory) ) console.log('State List:', filteredRecipes) console.log('Updated List:', newFilteredList) setFilteredRecipes(newFilteredList) }
当前代码的问题
- 每个
RecipeCategory组件单独维护选中状态,父组件没有统一管理所有选中分类,导致过滤逻辑只能基于单个分类,无法合并多个选中分类的结果 ChangeRecipeList每次只处理当前分类,直接覆盖之前的过滤结果,无法实现"多选分类时展示所有匹配任意选中分类的食谱"的需求
正确实现方案
步骤1:父组件统一管理选中状态
将选中状态从子组件移到父组件,用数组维护所有选中的分类,同时通过useEffect监听选中状态变化,自动触发过滤逻辑:
import { useState, useEffect } from 'react'; const recipes = [ // 你的食谱数据,保持不变 ]; const recipesCategories = ['Asian', 'Indian', 'SeaFood', 'Quick & Easy', 'Chicken']; export default function RecipeApp() { // 初始全选所有分类 const [selectedCategories, setSelectedCategories] = useState(recipesCategories); const [filteredRecipes, setFilteredRecipes] = useState(recipes); // 切换分类选中状态的回调 const toggleCategory = (category) => { setSelectedCategories(prev => { if (prev.includes(category)) { // 已选中则移除 return prev.filter(c => c !== category); } else { // 未选中则添加 return [...prev, category]; } }); }; // 选中分类变化时重新过滤食谱 useEffect(() => { // 若无选中分类,展示所有食谱(可根据需求调整) if (selectedCategories.length === 0) { setFilteredRecipes(recipes); return; } // 筛选出标签匹配任意一个选中分类的食谱 const filtered = recipes.filter(recipe => recipe.tags.some(tag => selectedCategories.includes(tag)) ); setFilteredRecipes(filtered); }, [selectedCategories]); return ( <div> <div className="category-buttons"> {recipesCategories.map(category => ( <RecipeCategory key={category} category={category} isSelected={selectedCategories.includes(category)} onToggle={() => toggleCategory(category)} /> ))} </div> <div className="recipe-slides"> {filteredRecipes.map(recipe => ( <RecipeSlide key={recipe.name} recipe={recipe} /> ))} </div> </div> ); }
步骤2:简化RecipeCategory子组件
子组件不再维护状态,仅负责渲染按钮和触发父组件的回调:
export default function RecipeCategory({ category, isSelected, onToggle }) { return ( <button onClick={onToggle} className={`category-btn ${isSelected ? 'active' : ''}`} > {category} </button> ); }
核心逻辑说明
- 父组件通过
selectedCategories数组统一管理所有选中的分类,确保状态一致性 - 利用
useEffect监听选中状态变化,自动执行过滤:recipe.tags.some()判断食谱是否包含任意一个选中的分类标签 - 子组件专注UI渲染,状态完全由父组件控制,避免了多组件状态不同步的问题
- 初始状态设置为全选所有分类,满足需求
内容的提问来源于stack exchange,提问作者Ibra
相关产品推荐
相关产品推荐

