简体   繁体   English

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

[英]How to delete an object from state when it is inside an array of objects

I'm trying to delete an object from an array within an object by the id.我正在尝试通过 id 从对象内的数组中删除一个对象。 I can't figure out how to do this...I've looked at several other posts but I feel like the set up is a little different so I still can't figure it out.我不知道如何做到这一点......我看过其他几篇文章,但我觉得设置有点不同,所以我仍然无法弄清楚。

deleteMeals doesn't delete the correct object even though I am grabbing the correct meal id.即使我获取了正确的膳食 ID, deleteMeals也不会删除正确的对象。 The "Monday" is also there as a placeholder / trying to figure things out. “星期一”也作为占位符/试图解决问题。

I had my store set up differently before, where each meal object had its own day key, and I was able to delete - except the first object (id: 1) would not delete until it was the last one.我之前的store设置不同,每个餐点对象都有自己的day键,并且我能够删除 - 除了第一个对象(id:1)在它是最后一个对象之前不会删除。 I ended up refactoring the store so that each meal object was in an array separated by the actual Day.我最终重构了商店,以便每个膳食对象都在一个由实际 Day 分隔的数组中。

So now I need help with two issues please!所以现在我需要帮助解决两个问题!

  1. How to properly delete given my store object set up给定我的商店对象设置,如何正确删除
  2. Why the proper meal won't delete为什么正餐不会删除
    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": [],
        }
   }

deleteMeal in App.js App.js 中的 deleteMeal

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
        }

Days Component天数组件

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>
        )

    }
}

Meals Component膳食成分

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} alt="Placeholder" />
                        </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

The recipe id appears to be extraneous, I don't think it is needed, you should be able to delete a meal by day and id.食谱 id 似乎无关紧要,我认为不需要,您应该可以按天和 id 删除一顿饭。

The idea is to shallowly copy the state object at each level you intend to update.这个想法是在您打算更新的每个级别浅复制状态对象。

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),
    },
  }));
}

Issue - Poor React Key问题 - 反应键不佳

You are using the array index as the react key in your Days component.您正在使用数组索引作为Days组件中的反应键。

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

If you for example had an array of three meals and you delete the first or second, the react keys would still be index 0 and 1. The length would change and the 3rd meal would no longer be rendered, but because the react key for the first two didn't change react bails on rerendering them.例如,如果您有一个包含三餐的数组并且您删除了第一餐或第二餐,则反应键仍将是索引 0 和 1。长度会改变并且第三餐将不再呈现,但是因为反应键是前两个没有改变重新渲染它们的反应保释金。

Lists and Keys列表和键

We don't recommend using indexes for keys if the order of items may change.如果项目的顺序可能发生变化,我们不建议对键使用索引。 This can negatively impact performance and may cause issues with component state.这会对性能产生负面影响,并可能导致组件状态出现问题。 Check out Robin Pokorny's article for an in-depth explanation on the negative impacts of using an index as a key .查看 Robin Pokorny 的文章, 深入了解使用索引作为键的负面影响

The important take-away from the linked article is this checklist链接文章的重要内容是此清单

  1. the list and items are static–they are not computed and do not change;列表和项目是静态的——它们不是计算出来的,也不会改变;
  2. the items in the list have no ids;列表中的项目没有 ID;
  3. the list is never reordered or filtered.该列表永远不会重新排序或过滤。

When all of them are met, you may safely use the index as a key .所有这些都满足时,您可以安全地使用索引作为键

Array indices are technically acceptable as react keys, so long as you don't mutate the array by inserting or deleting elements from it.数组索引技术上是可以接受的作为反应键,只要你通过插入或删除元素来改变数组。 Since your meal objects have an id property it should be used for the react key.由于您的meal对象具有id属性,因此应将其用于反应键。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM