How does edit in Recipe Box work

I’m struggling with my recipe box. I don’t understand how I can get my modal to show the current name and ingredients.

var Accordion = ReactBootstrap.Accordion;
var Panel = ReactBootstrap.Panel;
var PanelGroup = ReactBootstrap.PanelGroup;
var Button = ReactBootstrap.Button;
var ButtonToolbar = ReactBootstrap.ButtonToolbar;
var Modal = ReactBootstrap.Modal;
var FormGroup = ReactBootstrap.FormGroup;
var ControlLabel = ReactBootstrap.ControlLabel;
var FormControl = ReactBootstrap.FormControl;

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      recipes: [
        {name: 'Spaghetti', ingredients: ['Noodles', 'Meatballs']},
        {name: 'Onion Pie', ingredients: ['Onion', 'Pie Crust']}
      ],
      showAdd: false,
      showEdit: false
    };
    this.showAddModal = this.showAddModal.bind(this);
    this.showEditModal = this.showEditModal.bind(this);
    this.deleteRecipe = this.deleteRecipe.bind(this);
    this.addRecipe = this.addRecipe.bind(this);
  }
  showAddModal() {
    this.setState({showAdd: !this.state.showAdd});
  }
  showEditModal() {
    this.setState({showEdit: !this.state.showEdit});
  }
  addRecipe(recipe) {
    let updatedRecipes = this.state.recipes.slice();
    updatedRecipes.push(recipe);
    this.setState({recipes: updatedRecipes});
    this.showAddModal();
  }
  deleteRecipe(index) {
    let updatedRecipes = this.state.recipes.slice();
    updatedRecipes.splice(index, 1);
    this.setState({recipes: updatedRecipes});
  }
  render() {
    const recipes = this.state.recipes;
    const currentIndex = 0;
    return(
      <div className="jumbotron">
        <PanelGroup accordion id="recipes">
          {recipes.map((recipe, index) => (
            <Panel eventKey={index} key={index}>
              <Panel.Heading>
                <Panel.Title toggle>{recipe.name}</Panel.Title>
              </Panel.Heading>
              <Panel.Body collapsible>
                {recipe.ingredients.map((ingredient, index) =>
                  <p key={index}>{ingredient}</p>
                )}
                <ButtonToolbar>
                  <Button bsStyle="warning" onClick={this.showEditModal}>Edit</Button>
                  <Button bsStyle="danger" onClick={() => {this.deleteRecipe(index)}}>Delete</Button>
                </ButtonToolbar>
              </Panel.Body>
              <Modal show={this.state.showEdit} onHide={this.showEditModal}>
                <Modal.Header closeButton>
                  <Modal.Title>Edit Recipe</Modal.Title>
                </Modal.Header>
                <Modal.Body>
                  <FormGroup controlId="formControlsName">
                    <ControlLabel>Recipe Name</ControlLabel>
                    <FormControl type="text" required inputRef={(ref) => {this.recipeName = ref}} placeholder="Enter Name" />
                  </FormGroup>
                  <FormGroup controlId="formControlsIngredients">
                    <ControlLabel>Recipe Ingredients</ControlLabel>
                    <FormControl componentClass="textarea" type="text" required inputRef={(ref) => {this.recipeIngredients = ref}} placeholder="Enter Ingredients(separate by commas)"  />
                  </FormGroup>
                </Modal.Body>
                <Modal.Footer>
                  <Button bsStyle="success">Save Recipe</Button>
                </Modal.Footer>
              </Modal>
            </Panel>
          ))}
        </PanelGroup>
        <Button bsStyle="primary" onClick={this.showAddModal}>Add Recipe</Button>
        <AddRecipe onShow={this.state.showAdd} onAdd={this.addRecipe} onAddModal={this.showAddModal} />
      </div>
    );
  }
};

class AddRecipe extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  handleSubmit(e) {
    e.preventDefault();
    const regExp = /\s*,\s*/;
    var newName = this.recipeName.value;
    var newIngredients = this.recipeIngredients.value.split(regExp);
    var newRecipe = {name: newName, ingredients: newIngredients};
    this.props.onAdd(newRecipe);
  }
  render() {
    return(
      <Modal show={this.props.onShow} onHide={this.props.onAddModal}>
        <Modal.Header closeButton>
          <Modal.Title>New Recipe</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <FormGroup controlId="formControlsName">
            <ControlLabel>Recipe Name</ControlLabel>
            <FormControl type="text" required inputRef={(ref) => {this.recipeName = ref}} placeholder="Enter Name" />
          </FormGroup>
          <FormGroup controlId="formControlsIngredients">
            <ControlLabel>Recipe Ingredients</ControlLabel>
            <FormControl componentClass="textarea" type="text" required inputRef={(ref) => {this.recipeIngredients = ref}} placeholder="Enter Ingredients(separate by commas)"  />
          </FormGroup>
        </Modal.Body>
        <Modal.Footer>
          <Button bsStyle="success" onClick={this.handleSubmit}>Save Recipe</Button>
        </Modal.Footer>
      </Modal>
    );
  }
};

The edit modal is within the panel group. If I set the value of the formcontrols to recipe.name and recipe.ingredients respectively, every edit modal will show the values of the latest recipe, not the values of the current recipe on the map function.

ReactDOM.render(<App />, document.getElementById('app'));

Please help. This is the only thing holding me back from completing this project.

You should put all of your modal markup into a component, then pass that component the recipe you want to render.

<MyModal recipe={this.state.recipes[index]} />

Then inside the modal, you can just assign the data however you want:


<h1>{this.props.recipe.name}</h1>
<ul>
    {this.props.recipe.ingredients.map(i => <li>{li}</li>);}
</ul>

This doesn’t solve the underlying issue. It simply rearranges the code. It still only shows the values for the last recipe regardless of which recipe I try to edit.

I was hoping to inspire you rather than just feed you an answer. One option is to have your edit button set “currentlyEditing” state property, then you’d render just that one index in the modal. Setup the button just like you have your delete button:

<Button bsStyle="warning" onClick= {() => this.showEditModal(index)}>Edit</Button>

Then change your showEditModal button:

showEditModal(index) {
    this.setState({currentlyEditing: index, showEdit: !this.state.showEdit});
  }

Then you can render the modal with data from this.state.recipes[this.state.currentlyEditing].

It works now. I had a similar idea but missed the setState({currentlyEditing: index}) part. Thank you so much.

1 Like