0

When I run this code, I get a bootstrap panel group for each recipe item inside of local storage. When I try to delete a recipe, sometimes the right recipe is removed and sometimes not. The console shows that the right recipe is removed from local storage but for some reason when the app component resets its state, the wrong recipe is removed. I've noticed that if I try to delete the recipes from the bottom up, it works. But if I click on the first recipe, the bottom recipe is removed.

I know this is an easy fix but I need a fresh perspective. Thanks everyone! Also, sorry lack of indentation in the code - stack overflow wasn't being too friendly with the spacing

class App extends Component {
  constructor() {
  super();
  this.deleteRecipe = this.deleteRecipe.bind(this)
  this.state = {
    recipeData: JSON.parse(localStorage.getItem('recipeData'))
 }
}

deleteRecipe() {
this.setState({recipeData: JSON.parse(localStorage.getItem('recipeData'))})
}

render() {
 return (
  <div className="App">
    <div className="App-header">
      <img src={logo} className="App-logo" alt="logo" />
      <h2>Welcome to React Recipe Box!</h2>
    </div>

    <div className="container">
      {this.state.recipeData.map(recipe => {
        return (
        <Recipe name={recipe.name} ingredients={recipe.ingredients} deleteRecipe={this.deleteRecipe}/>
        )
      })}
    </div>
  </div>
);
}
}

class Recipe extends Component {
 constructor() {
 super();
  this.onDeleteRecipe = this.onDeleteRecipe.bind(this)
 }

componentWillMount(){   //set state when component is about to mount
this.state = {
  name: this.props.name,
  ingredients: this.props.ingredients,

}
}

onDeleteRecipe() {
var recipeList = JSON.parse(localStorage.getItem('recipeData'));
for(var i = 0; i < recipeList.length; i++) {
  if(recipeList[i].name === this.state.name) {
    recipeList.splice(i, 1);
    console.log("Deleted " + this.state.name, recipeList);
    localStorage.removeItem('recipeData');
    localStorage.setItem('recipeData', JSON.stringify(recipeList));

    this.props.deleteRecipe();
  }
 }

}

 render() {
 return (
 <div>
  <div className="panel-group">
    <div className="panel panel-primary">
      <div className="panel-heading">
        <h2 className="panel-title">
          <a data-toggle="collapse" data-target={'#' + (this.state.name).replace(/\s/g, '')} href={'#' + (this.state.name).replace(/\s/g, '')}>
             {this.state.name}
          </a>
        </h2>>              
      </div>
     <div id={(this.state.name).replace(/\s/g,'')} className="panel-collapse collapse">
      <div className="panel-body"> 
          {this.state.ingredients.map(ingredient => {
            return <li className="list-group-item">{ingredient}</li>
          })}
          <div className="btn-group">
            <button className="btn btn-sm btn-info" data-toggle="modal" 
                    data-target={'#' + (this.state.name).replace(/\s/g, '') + 'EditModal'}>Edit</button>
            <button className="btn btn-sm btn-danger" data-toggle="modal"
                    data-target={'#' + (this.state.name).replace(/\s/g, '') + 'RemoveModal'}
                    >Delete</button>
          </div>
      </div>
     </div>
    </div>
  </div>


    <div className="modal modal-lg" id={(this.state.name).replace(/\s/g, '') + 'EditModal'} >
        <div className="modal-content">
          <div className="modal-header">
            <h2>Edit {this.state.name}</h2>
          </div>
          <div className="modal-body">
            <ul className="list-group list-unstyle">
              {this.state.ingredients.map( ingredient => {
                return <li className="list-group-item">{ingredient}</li>
              })}
            </ul>
          </div>
          <div className="modal-footer">
            <div className="btn-group">
              <button className="btn btn-sm btn-info"  data-dismiss="modal">Save</button>
              <button className="btn btn-sm btn-danger" data-dismiss="modal">Close</button>
            </div>
          </div>
        </div>
      </div>


      <div className="modal modal-lg" id={this.state.name.replace(/\s/g, '') + 'RemoveModal'}>
        <div className="modal-content">
          <div className="modal-body">
            <h3>This will remove the selected recipe. Are you sure?</h3>
          </div>
          <div className="modal-footer">
            <div className="btn-group">
              <button className="btn btn-sm btn-danger" data-dismiss="modal" onClick={this.onDeleteRecipe}>Delete</button>
              <button className="btn btn-sm btn-info" data-dismiss="modal">Close</button>
            </div>
          </div>
        </div>
      </div>
 </div>
);
}
}

export default App;
Zachary Bennett
  • 932
  • 9
  • 15

1 Answers1

1

I'm still a novice, but... I built a react recipe box for FreeCodeCamp a few months ago, so I just pulled it up to compare my delete function to yours.

I notice that you treat localStorage differently than I did in mine. (Not that my way is best or even right!) I wonder if somehow the problem is rooted in that design. Your delete function goes into localStorage and makes changes, then you run a setState to sort of "re-get" the recipeData, if I'm reading right?

By contrast, my app declares a variable from localStorage and I set this.state.recipeArray to equal that variable. Then all of my edit/add/delete functions change this.state.recipeArray, not localStorage. Sorta like this:

handleDelete: function(e) {
    e.preventDefault();

    var replacementRecipeArray = this.state.recipeArray.filter((recipe) => recipe.title !== e.target.name);
    //dot-filter with ES6 fat-arrow returns all items where title doesn't match the one we clicked; IOW, it removes the one we want to delete

    this.setState({
      recipeArray: replacementRecipeArray
    });

  }

In order to get any changes to this.state.recipeArray back to localStorage, I do a localStorage.setItem every time I render the page.

render() {
    //first save current array to localstorage - wasn't reliable anywhere else
    localStorage.setItem("_shoesandsocks_recipes", JSON.stringify(this.state.recipeArray));

    //render page
    return (
      <div className="App">
        <div className="recipeContainer"> 
          // etc etc

For all I know this is a crazy design, but it works.

R.L. Brown
  • 51
  • 1
  • 4
  • Tried it your way and, unfortunately, I get the same thing... Thanks for the response though! Tried to up your answer but I haven't hit 15 points yet :/ – Zachary Bennett Jan 25 '17 at 20:18
  • The state of the parent component and localstorage are changing correctly, but its deleting the wrong component for some reason. Maybe the state is being corrupted as its passed to the child - but I don't understand how. – Zachary Bennett Jan 25 '17 at 20:22
  • Do you get a warning about missing keys, when you iterate your list of recipes? I tried pulling your code into a separate page of the sample app I'm working on, and I haven't gotten it all the way working (i don't have bootstrap pulled in, for example), but plugged in some localStorage 'recipeData' following your app, and it renders it on the page. In the console I get a warning about keys, though. Maybe not having keys can tangle which object gets deleted? [see React docs on keys, here](https://facebook.github.io/react/docs/lists-and-keys.html#keys) – R.L. Brown Jan 25 '17 at 21:19
  • I do get that warning. I tried it with keys before but I'll look into it some more - thanks for the link. I really appreciate it – Zachary Bennett Jan 25 '17 at 23:26
  • You know what? It worked! I just passed the name of the recipe as the key value and it works perfectly. Apparently when I tried it earlier parts of my code were different and something else was wrong. Thank you so much ! This link gave me a bit more info too - http://stackoverflow.com/questions/30406811/removing-an-item-causes-react-to-remove-the-last-dom-node-instead-of-the-one-ass – Zachary Bennett Jan 25 '17 at 23:33
  • Funny you linked to that Q; I just a few days ago learned that my habit of using 'index' for key was a bad practice... – R.L. Brown Jan 25 '17 at 23:56
  • yeah there's so much to learn. I'm sure half of what I'm doing is wrong.. haha! Good luck to you! – Zachary Bennett Jan 26 '17 at 01:04