当对象位于对象数组中时如何从状态中删除对象

Posted

技术标签:

【中文标题】当对象位于对象数组中时如何从状态中删除对象【英文标题】:How to delete an object from state when it is inside an array of objects 【发布时间】:2021-01-12 07:41:50 【问题描述】:

我正在尝试通过 id 从对象内的数组中删除一个对象。我不知道该怎么做……我看过其他几篇帖子,但我觉得设置有点不同,所以我还是不明白。

deleteMeals 不会删除正确的对象,即使我获取了正确的餐点 ID。 “星期一”也作为占位符/试图解决问题。

我之前的 store 设置不同,每个餐点对象都有自己的 day 键,我可以删除 - 除了第一个对象 (id: 1) 直到最后一个对象才会删除一。我最终重构了商店,以便每个用餐对象都在一个由实际 Day 分隔的数组中。

所以现在我需要帮助解决两个问题!

    如何根据我的商店对象设置正确删除 为什么正确的饭菜不会删除
    const store = 
    
       meals: 
            "Monday": [
                
                    "id": 1,
                    "recipe_id": 2,
                     "user_id": 1
                ,
                
                    "id": 2,
                    "recipe_id": 1,
                    "user_id": 1
                ,
                
                    "id": 3,
                    "recipe_id": 3,
                    "user_id": 1
                
    
            ],
            "Tuesday": [
                
                    "id": 4,
                    "recipe_id": 4,
                    "user_id": 1
                
            ],
            "Wednesday": [],
        
   

在 App.js 中删除餐食

deleteMeal = (day, mealId) => 
  this.setState(prevState => (
  // copy existing state
  ...prevState,
  // update meals key
  meals: 
      // copy existing meals state
      ...prevState.meals,
     // update day key & filter meals array by id
     [day]: prevState.meals[day].filter( ( id ) => id !== mealId),
  
 ));


const contextValue = 
            recipes: this.state.recipes,
            meals: this.state.meals,
            addRecipe: this.addRecipe,
            deleteRecipe: this.deleteRecipe,
            updateRecipe: this.updateRecipe,
            addMeal: this.addMeal,
            deleteMeal: this.deleteMeal
        

天数组件

class Days extends React.Component
    render() 
        const  day, meals  = this.props
        let mealsList;

        if (meals[day]) 
            mealsList = meals[day]
                .map((meal, key) => 
                    <Meals 
                        key=key
                        day=day
                        meal=meal
                    />
            )
        
        
        return(
            <div className="Days">
                <h3>day</h3>
                meals && meals[day] && mealsList
            </div>
        )

    

膳食组件

class Meals extends React.Component 
    static contextType = StashContext;

    state = 
        recipeTitle: 'No Title',
        recipeImageUrl: '',
        showModal: false
    

    findRecipe = (recipeId, recipes) => 
        const recipe = recipes.find( recipe => recipe.id == recipeId)

        if (recipe) 
            this.setState(
                recipeTitle: recipe.title,
                recipeImageUrl: recipe.image_url
            )
        
    

    componentDidMount() 
        const  recipes  = this.context
        const  meal  = this.props

        this.findRecipe(meal.recipe_id, recipes)
    

    handleClickDelete = (event) => 
        const  day, meal  = this.props
        const  id  = meal
        event.preventDefault()
        this.context.deleteMeal(day, id)
    

    toggleModal = () => 
        this.setState(
            showModal: !this.state.showModal
        )
    

    render()
        const  recipeTitle, recipeImageUrl, showModal  = this.state;
        const  meal  = this.props;

        const customStyles = 
            overlay: 
                background: '#4c645682'
            ,
            content: 
                background: 'rgb(240 240 240)'
            
        

        const permissions = 
            edit: false,
            add: false,
            delete: false
        

        return (
            <div className="Meals">
                showModal && 
                    <DisplayModal 
                        meal=meal 
                        customStyles=customStyles 
                        showModal=showModal
                        closeModal=this.toggleModal
                        label="Meal Modal"
                        permissions=permissions
                    />
                

                <div className="Meals__info">
                    <div className="info_box left">
                        <div className="Meals__img">
                            <img src=recipeImageUrl  />
                        </div>
                    </div>

                    <div className="info_box middle">
                        <div className="Meals__recipe-title">
                            recipeTitle
                        </div>
                    </div>
                    <div className="info_box right">
                        <div className="Meals__options">
                            <FontAwesomeIcon
                                icon=faEye 
                                onClick=e => this.setState(showModal: true) />
                            <FontAwesomeIcon 
                                icon=faBackspace 
                                onClick=this.handleClickDelete />
                        </div>
                    </div>
                </div>
            </div>
        )
    


export default Meals

【问题讨论】:

为什么需要餐食和食谱 ID?一天中的两个元素可以有相同的用餐ID吗? 我想我已经回答了 (2) 但对于 (1) 我认为我们需要更多关于您的商店如何与您的 UI(即组件)相关的上下文,大概与删除功能相关,虽然目前尚不清楚为什么您可能有两个事实来源。 【参考方案1】:

recipe id 似乎是多余的,我认为不需要,您应该可以删除一天中的餐点和 id。

这个想法是在您打算更新的每个级别浅层复制状态对象。

deleteMeal = (day, mealId) => 
  this.setState(prevState => (
    // copy existing state
    ...prevState,
    // update meals key
    meals: 
      // copy existing meals state
      ...prevState.meals,
      // update day key & filter meals array by id
      [day]: prevState.meals[day].filter(( id ) => id !== mealId),
    ,
  ));

问题 - React Key 不佳

您正在使用数组索引作为 Days 组件中的反应键。

meals[day].map((meal, key) => (
  <Meals 
    key=key // <-- array index!!
    day=day
    meal=meal
  />
))

例如,如果您有一个包含三餐的数组,并且您删除了第一餐或第二餐,那么反应键仍将是索引 0 和 1。长度会改变,第三餐将不再呈现,但因为反应前两个的关键在重新渲染它们时没有改变反应保释。

Lists and Keys

我们不建议使用索引作为键,如果项目的顺序可能 改变。这可能会对性能产生负面影响,并可能导致问题 与组件状态。查看 Robin Pokorny 的文章 in-depth explanation on the negative impacts of using an index as a key.

链接文章的重要内容是这份清单

    列表和项目是静态的——它们不会被计算也不会改变; 列表中的项目没有 ID; 列表永远不会重新排序或过滤。

当满足所有条件时,您可以安全地使用索引作为键

数组索引在技术上可以作为反应键,只要您不要通过插入或删除元素来改变数组.由于您的 meal 对象具有 id 属性,因此它应该用于反应键。

meals[day].map(meal => (
  <Meals 
    key=meal.id
    day=day
    meal=meal
  />
))

【讨论】:

嗯,我将不得不更多地研究 prevState,因为我以前没有使用过它。要回答您关于 recipeId 的其他评论 --- 我将 recipe 对象留在了商店之外,因为我认为它可能与删除餐点无关?它只是一个 id 来引用餐食的食谱(用于显示它)使用您的解决方案,我可以删除餐食对象......但它仍然没有删除正确的对象。 我刚刚发现,无论我点击删除哪一餐,都会第一天删除最后一餐。所以对于周一的餐点,如果我点击餐点 id 1,它会删除 id 3,然后是 2,然后是 1 @staysee 也许处理程序没有被传递正确的值。您能否更新您的问题以包含您的组件代码以及您如何映射您的 UI 并附加 deleteMeal 作为处理程序? 嗨,Drew,我刚刚用 Meals 组件更新了我的问题! @staysee 我没看错吧,Meals 组件只渲染一个饭菜对象? deleteMeal (this.context.deleteMeal) 在 handleClickDelete 中通过 this.props.daythis.props.meal.id 调用?您是否在处理程序中记录了餐点 ID 以确保它是您想要的?然后再次登录deleteMeal?你能分享上下文代码吗?您在那里删除的方式可能有些愚蠢。

以上是关于当对象位于对象数组中时如何从状态中删除对象的主要内容,如果未能解决你的问题,请参考以下文章

如何通过单击按钮从反应状态挂钩数组中删除对象

当对象中的字段发生更改时如何从 v-model 数组中删除对象

从对象 JSONB 中的数组中删除元素

如何从数组中删除重复值?当数组内的对象属性未定义时,我的代码失败

当“内容数组”-Binding 获取内容时,如何从 ArrayController 中删除对象?

如何更改数组状态容器中的对象属性? React Hooks 使用状态