TypeError: Cannot read property 'recipeName' of undefined

TypeError: Cannot read property 'recipeName' of undefined

我现在用细齿梳检查了我的代码,但我似乎看不出哪里没有定义 "recipe" 属性。我希望一些更有经验的眼睛能帮助我找出我犯了错误的地方。任何帮助将不胜感激。谢谢你。

Ps。请在下面找到我的代码...这是来自 FreeCodeCamp 的 Recipe Box 项目,我遵循了来自 CodingTutorials360 的 Dylan Israel 的演练。据我所知,除了文档规定的对 React-Bootstrap 的一些更改外,我的代码与他的代码相同。

import React, { Component } from 'react';
import './App.css';
import Panel from 'react-bootstrap/lib/Panel'
import Button from 'react-bootstrap/lib/Button'
import ButtonToolbar from 'react-bootstrap/lib/ButtonToolbar'
import Modal from 'react-bootstrap/lib/Modal'
import FormGroup from 'react-bootstrap/lib/FormGroup'
import ControlLabel from 'react-bootstrap/lib/ControlLabel'
import FormControl from 'react-bootstrap/lib/FormControl'
import PanelGroup from 'react-bootstrap/lib/PanelGroup'



class App extends Component {

  state = {
    showAdd: false,
    showEdit: false,
    currentIndex: 0,
    recipes: [

    ],
    newestRecipe: {recipeName:"", ingredients: []}
  }

  deleteRecipe(index){
    let recipes = this.state.recipes.slice();
    recipes.splice(index, 1);
    localStorage.setItem('recipes', JSON.stringify(recipes));
    this.setState({recipes});
  }

  updateNewRecipe(value, ingredients){

    this.setState({newestRecipe:{recipeName: value, ingredients: ingredients}});
  }

  close = () => {
    if(this.state.showAdd){
      this.setState({showAdd: false});
    } else if(this.state.showEdit){
      this.setState({showEdit: false});
    }
  }

  open = (state, currentIndex) => {
    this.setState({[state]: true});
    this.setState({currentIndex});

  }

  saveNewRecipe = () => {
    let recipes = this.state.recipes.slice();
    recipes.push({recipeName: this.state.newestRecipe.recipeName, ingredients: this.state.newestRecipe.ingredients});
    localStorage.setItem('recipes', JSON.stringify(recipes));
    this.setState({ recipes });
    this.setState({newestRecipe: {recipeName: '', ingredients:[]}});
    this.close();
  }

  updateRecipeName(recipeName, currentIndex){
    let recipes = this.state.recipes.slice();

    recipes[currentIndex] = {recipeName: recipeName, ingredients: recipes[currentIndex].ingredients};
    this.setState({recipes});
    localStorage.setItem('recipes', JSON.stringify(recipes));
    this.close();
  }

  updateIngredients(ingredients, currentIndex){
    let recipes = this.state.recipes.slice();
    recipes[currentIndex] = {recipeName: recipes[currentIndex].recipeName, ingredients: ingredients};
    localStorage.setItem('recipes', JSON.stringify(recipes));
    this.setState({recipes});
  }

  componentDidMount(){
    let recipes = JSON.parse(localStorage.getItem("recipes")) || [];
    this.setState({recipes});
  }

  render() {
    const {recipes, newestRecipe, currentIndex} = this.state;
    return (
      <div className="App container" id="display-box">
        {recipes.length > 0 && (
          <div>
            <PanelGroup accordion id="recipe-list" defaultActiveKey="2">
            {recipes.map((recipe, index)=>(
              <Panel eventKey={index} key={index}>
                <Panel.Heading>
                  <Panel.Title toggle>{recipe.recipeName}</Panel.Title>
                </Panel.Heading>
                <Panel.Body collapsible>
                  <ol>
                    {recipe.ingredients.map((item)=>(
                      <li key={item}>{item}</li>
                    ))}
                  </ol>
                  <ButtonToolbar>
                    <Button bsStyle="danger" onClick={(event)=>this.deleteRecipe(index)}>Delete Recipe</Button>
                    <Button bsStyle="default" onClick={(event) => this.open("showEdit", index)}>Edit Recipe</Button>
                  </ButtonToolbar>
                </Panel.Body>
              </Panel>
              ))}
            </PanelGroup>
          </div>
        )}

        <Modal show={this.state.showEdit} onHide={this.close}>
          <Modal.Header closeButton>
            <Modal.Title>Edit Recipe</Modal.Title>
          </Modal.Header>
          <Modal.Body>
            <FormGroup controlId="formBasicText">
              <ControlLabel>Recipe Name</ControlLabel>
              <FormControl
                type="text"
                value={recipes[currentIndex].recipeName}
                placeholder="Enter Text" onChange={(event) => this.updateRecipeName(event.target.value, currentIndex)}
              />
            </FormGroup>

            <FormGroup controlId="formControlsTextarea">
              <ControlLabel>Ingredients</ControlLabel>
              <FormControl 
                componentClass="textarea"
                onChange={(event) => this.updateIngredients(event.target.value.split(","), currentIndex)}
                placeholder="Enter Ingredients [Seperate by Commas]"
                value={recipes[currentIndex].ingredients}>
              </FormControl>
            </FormGroup>
            <Modal.Footer>
              <Button bsStyle="primary" onClick={(event) => this.saveNewRecipe()}>Save Changes</Button>
            </Modal.Footer>
          </Modal.Body>
        </Modal>

        <Modal show={this.state.showAdd} onHide={this.close}>
          <Modal.Header closeButton>
            <Modal.Title>Add Recipe</Modal.Title>
          </Modal.Header>
          <Modal.Body>
            <FormGroup controlId="formBasicText">
              <ControlLabel>Recipe Name</ControlLabel>
              <FormControl
                type="text"
                value={newestRecipe.recipeName}
                placeholder="Enter Recipe Name"
                onChange={(event) => this.updateNewRecipe(event.target.value, newestRecipe.ingredients)}
              >
              </FormControl>
            </FormGroup>
            <FormGroup controlId="formControlTextarea">
              <ControlLabel>Ingredients</ControlLabel>
              <FormControl
                type="textarea"
                placeholder="Enter Ingredients [Seperate by Commas]"
                onChange={(event) => this.updateNewRecipe(newestRecipe.recipeName, event.target.value.split(','))}
                value={newestRecipe.ingredients}
              >
              </FormControl>
            </FormGroup>
          </Modal.Body>
          <Modal.Footer>
            <Button onClick={(event) => {this.saveNewRecipe()}}>Save</Button>
          </Modal.Footer>
        </Modal>

        <Button bsStyle="primary" onClick={(event)=>this.open("showAdd", currentIndex)}>Add Recipe</Button>
      </div>
    );
  }
}

export default App;

如果我错了请纠正我,但看起来您没有正确定义初始状态,这可能是您收到未定义错误的原因。

通常,状态最初是在构造函数中定义的,而不是作为一个单独的对象。当我尝试像您一样定义状态时,我在 codepen 上遇到错误。 查看他们如何在此作弊中定义状态 sheet(在状态下):https://devhints.io/react.

我经历过相同的 FreeCodeCamp 反应项目,其中一些可能需要很长时间才能弄清楚,但最终它真的值得。希望这会帮助您找到错误:)

问题是当没有当前配方时,您正在访问(两次)当前配方数据。那是因为,虽然您没有显示 "modal",但您正在渲染它。

一个可能的解决方案是仅当您正在编辑某些内容时才呈现该模态,您可以将第 110-139 行替换为:

{ this.state.showEdit &&
      <Modal show={this.state.showEdit} onHide={this.close}>
        <Modal.Header closeButton>
          <Modal.Title>Edit Recipe</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <FormGroup controlId="formBasicText">
            <ControlLabel>Recipe Name</ControlLabel>
            <FormControl
              type="text"
              value={recipes[currentIndex].recipeName}
              placeholder="Enter Text" onChange={(event) => this.updateRecipeName(event.target.value, currentIndex)}
            />
          </FormGroup>

          <FormGroup controlId="formControlsTextarea">
            <ControlLabel>Ingredients</ControlLabel>
            <FormControl 
              componentClass="textarea"
              onChange={(event) => this.updateIngredients(event.target.value.split(","), currentIndex)}
              placeholder="Enter Ingredients [Seperate by Commas]"
              value={recipes[currentIndex].ingredients}>
            </FormControl>
          </FormGroup>
          <Modal.Footer>
            <Button bsStyle="primary" onClick={(event) => this.saveNewRecipe()}>Save Changes</Button>
          </Modal.Footer>
        </Modal.Body>
      </Modal>
    }

请注意,我唯一做的就是添加该代码块的第一行和最后一行。

您应该使用构造函数来定义状态并绑定您的函数 saveNewRecipe

constructor(props) {
  super(props);

  state = {
    showAdd: false,
    showEdit: false,
    currentIndex: 0,
    recipes: [

    ],
    newestRecipe: {recipeName:"", ingredients: []}
  };

  this.saveNewRecipe = this.saveNewRecipe.bind(this);
}