TypeError:无法读取属性' recipeName'未定义的

时间:2018-03-14 12:04:47

标签: javascript reactjs react-bootstrap react-dom

我现在用细齿梳子完成了我的代码,我似乎无法看到"配方"属性未定义。我希望一些更有经验的眼睛可以帮助我,找出我犯错的地方。任何帮助将不胜感激。谢谢。

聚苯乙烯。请在下面找到我的代码......它是来自FreeCodeCamp的Recipe Box项目,我跟随Dylan Israel从CodingTutorials360开始。据我所知,除了文档规定的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;

3 个答案:

答案 0 :(得分:0)

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

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

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

答案 1 :(得分:0)

问题是当没有当前配方时,您正在访问(两次)当前配方数据。这是因为,虽然你没有显示“模态”,但你正在渲染它。

一种可能的解决方案是仅在编辑某些内容时渲染该模态,您可以使用此替换第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>
    }

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

答案 2 :(得分:0)

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

constructor(props) {
  super(props);

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

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

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