首页 > 解决方案 > 当对象位于对象数组中时如何从状态中删除对象

问题描述

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

deleteMeals即使我获取了正确的用餐 ID,也不会删除正确的对象。“星期一”也作为占位符/试图弄清楚事情。

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

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

  1. 给定我的商店对象设置如何正确删除
  2. 为什么正确的饭菜不会删除
    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

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

标签: javascriptarraysreactjsnestedjavascript-objects

解决方案


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

问题 - 不良反应键

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

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

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

列表和键

如果项目的顺序可能发生变化,我们不建议对键使用索引。这会对性能产生负面影响,并可能导致组件状态出现问题。查看 Robin Pokorny 的文章, 深入解释使用索引作为键的负面影响

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

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

所有这些都满足时,您可以安全地使用索引作为 key

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

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

推荐阅读