Claude
Claude

Reputation: 391

component dont rerender when I remove item from state but it rerender when I add item

I have a Liked method that checks for an existing item in an array, depending on the return value, component Recipe_serving display conditionally. What I notice here is when I add an item into the array (likedList) child component rerender and condition is check, but when I remove item re-render doesn't occur even though setState is used


class App extends Component {
  state = {
    result:[],
    recipe:{},
    isActive:"47746",
    addToShopping: [],
    likedList:[]
  }

  setActiveId=(id)=>{
    this.getRecipe(id)
  }

  pushToLikeList = () => {
    // if id is in likeList then remove
    // if id not in likeList then add
    if(this.state.likedList.findIndex(el=>el.recipe.recipe_id===this.state.isActive) === -1){
      this.setState(prevState => {
        return {
          likedList: [prevState.recipe, ...prevState.likedList]
        }
      })
    }else{
      this.setState(prevState=>{
        const index = prevState.likedList.findIndex(el=>el.recipe.recipe_id===prevState.isActive);
        prevState.likedList.splice(index,1)
        return{
          likedList:prevState.likedList
        }
      })

    }


  }
  Liked=()=>{
    return (this.state.likedList.findIndex(el=>el.recipe.recipe_id===this.state.isActive) !== -1)
  }
  render() {

    return (
      <div className="App">

        <Recipe_serving 
        isActive={this.state.isActive}
        likedList={this.state.likedList}
        pushToLikeList={this.pushToLikeList}
        Liked={this.Liked}
        />
      </div>
    );
  }
}

const Recipe_serving = (props)=>{

    return(
        <div>
            <button className="recipe__love" onClick={props.pushToLikeList}>
                <svg className="header__likes">
                    <use href={props.Liked?"img/icons.svg#icon-heart":"img/icons.svg#icon-heart-outlined"}></use>
                </svg>
            </button>
        </div>
    )
}

Upvotes: 3

Views: 2513

Answers (1)

Cerberus
Cerberus

Reputation: 10218

The key is that React checks for the props/state changes only shallowly - any subobjects (and arrays, since they're objects) are checked by reference, not by value.

When you add the item using the spread operator, you're essentially creating a brand new array by cloning the current one, so React notices this and triggers the rerender. When you delete the item, however, it's different, because splice works in-place, and the reference to array stays the same.

How this could be resolved:

  1. Use slice to clone the prevState.likedList before deleting the item with splice.

  2. Use filter to create a new array with all existing elements except one.

  3. Use spread operator, as such:

return {
    likedList: [...prevState.likedList.slice(0, index), ...prevState.likedList.slice(index + 1)]
}
  1. Finally, you can just forceUpdate() the component after state is set (not recommended, since it breaks the semantics, but possible).

Upvotes: 7

Related Questions