将数据传递给父级 - recipebook React

时间:2017-08-03 14:40:52

标签: javascript reactjs

我正在处理食谱书应用程序并尝试实现编辑输入功能。 它是如何工作的是输入食谱名称(例如意大利面),然后是成分(例如鸡蛋,面粉,盐)。成分必须以逗号输入,并将显示为列表。

  • 面食
  • -Egg
  • -Flour

我可以看到它有点工作,因为我可以看到输入文本中的新条目(例如,最初是鸡蛋,面粉,盐 - >鸡蛋,面粉,盐,水)当我再次尝试编辑它时

然而,额外的成分(在上面的例子中:水)没有出现在列表中。我是否必须想办法重新制作清单?

更新 我想我知道错误可能在哪里。传递数据和设置状态存在一些问题。

<EditRecipe recipe={this.props.recipe} editRecipe={this.editRecipe.bind(this, this.props.recipe.id, recipe)}/>

App.js

    import React, { Component } from 'react';
// import logo from './logo.svg';
import './App.css';
import uuid from 'uuid';
import Modal from 'react-modal';
import RecipeList from './components/RecipeList/RecipeList';
import AddRecipe from './components/AddRecipe/AddRecipe';

class App extends Component {

  constructor(props){
    super(props);
    this.state = {
      recipes:[]
    };
  }

  getRecipes(){
    this.setState({recipes:[
      {
        id: uuid.v4(),
        food: "pumpkin pie",
        ingredients: ["pumpkin puree", "sweetened condensed milk", "eggs", "pumpkin pie spice", "pie crust"]
      },
      {
        id: uuid.v4(),
        food: "spaghetti",
        ingredients: ["noodles", "tomato sauce", "meatballs"]
      },
      {
        id: uuid.v4(),
        food: "onion pie",
        ingredients: ["onion", "pie crust"]
      },

    ]});
  }

  componentWillMount(){
    this.getRecipes();
  }

  handleAddRecipe(recipe){
    let recipes = this.state.recipes;
    recipes.push(recipe);
    this.setState({recipes: recipes});
  }

  handleDeleteRecipe(id){
    let recipes = this.state.recipes;
    let index = recipes.findIndex(x => x.id === id);
    recipes.splice(index,1);
    this.setState({recipes: recipes});
  }

  handleEditRecipe(id, recipe){
    let recipes = this.state.recipes;
    let index = recipes.findIndex(x => x.id === id);
    recipes.splice(index,1,recipe);
    this.setState({recipes: recipes});
  }

  render() {
    return (
      <div className="App">
        <RecipeList  recipes={this.state.recipes} onDelete={this.handleDeleteRecipe.bind(this)} onEdit={this.handleEditRecipe.bind(this)}/>
        <AddRecipe addRecipe={this.handleAddRecipe.bind(this)}/>
      </div>
    );
  }
}

export default App;

RecipeList.js

import React, { Component } from 'react';
import Collapsible from 'react-collapsible';
import RecipeItem from '../RecipeItem/RecipeItem'
import './RecipeList.css';

class RecipeList extends Component{

deleteRecipe(id){
  this.props.onDelete(id);
}

editRecipe(id, recipe){
  this.props.onEdit(id, recipe);
}

  render(){

    let recipeItem;

    if(this.props.recipes){
      recipeItem=this.props.recipes.map(recipe => {
        return(
          <RecipeItem onEdit={this.editRecipe.bind(this)} onDelete={this.deleteRecipe.bind(this)} key={recipe.id} recipe={recipe} />
        )
      });
    }

    return(
      <div className="recipeList box">
      {recipeItem}
      </div>
    )
  }
}

export default RecipeList;

RecipeItem.js

import React, { Component } from 'react';
import Collapsible from 'react-collapsible';
import EditRecipe from '../EditRecipe/EditRecipe';

class RecipeItem extends Component{

  deleteRecipe(id){
    this.props.onDelete(id);
  }

  editRecipe(id, recipe){
    this.props.onEdit(id, recipe);
  }

  render(){

    let recipe=this.props.recipe
    let foodName=recipe.food;
    let ingredientItem;

    if(recipe.ingredients){
      ingredientItem=recipe.ingredients.map(ingredient=>{
        return(
          <a className="panel-block">
            {ingredient}
          </a>
        )
      })
    }
    return(
      <ul>
        <li className="Recipe">
        <Collapsible trigger={foodName} transitionTime="200" easing="ease-in-out">
        <nav className="panel">
          <p className="panel-heading">
            Ingredients
          </p>
          {ingredientItem}
          <div className="panel-block">
            <button className="button is-warning is-outlined" onClick={this.deleteRecipe.bind(this, this.props.recipe.id)}>
              Delete
            </button>
            <EditRecipe recipe={this.props.recipe} editRecipe={this.editRecipe.bind(this, this.props.recipe.id, recipe)}/>
          </div>
          </nav>
        </Collapsible>
        </li>
      </ul>
    );
  }
}

export default RecipeItem;

EditRecipe.js

import React, { Component } from 'react';
import RecipeForm from '../RecipeForm/RecipeForm';
// import './EditRecipe.css';
import Modal from 'react-modal';
import uuid from 'uuid';
// import Modal from 'boron/DropModal';
// import './RecipeList.css';

class RecipeEdit extends Component{

  constructor(props){
    super(props);
    this.state = {
      revisedRecipe:{
        id: this.props.recipe.id,
        food: this.props.recipe.food,
        ingredients: this.props.recipe.ingredients
      },
      modalIsOpen: false,
      speed: 100
    };
    this.openModal = this.openModal.bind(this);
    this.closeModal = this.closeModal.bind(this);
  }

  openModal(){
    this.setState({modalIsOpen: true});
  }

  closeModal(){
    this.setState({modalIsOpen: false});
  }

  handleSubmit(e){

    const revised = this.state.revisedRecipe;

    this.props.editRecipe(revised);

    e.preventDefault();
  }


  handleNameChange(e){
    this.setState({revisedRecipe:{
      food: e.target.value
    }
  });
  }

  handleIndChange(e){
    this.setState({revisedRecipe:{
      ingredients: e.target.value
    }
  });
  }



  render(){
    const speed = this.state.speed;
    let recipe=this.props.recipe;
    let foodName=this.state.revisedRecipe.food;
    let ingredients=recipe.ingredients;

    return(
      <div>
        <button className="button is-primary" onClick={this.openModal}>Edit Recipe</button>
        <Modal
          isOpen={this.state.modalIsOpen}
          onAfterOpen={this.afterOpenModal}
          onRequestClose={this.closeModal}
          closeTimeoutMS={speed}
          contentLabel="Example Modal"
        >
        <div className="field">
          <h2 className="title is-2">Edit Recipe</h2>
          <form>
            <label className="label">Recipe</label>
            <div className="control">
              <input className="input" type="text" placeholder="Recipe Name" ref="recipeName" value={this.state.revisedRecipe.food} onChange={this.handleNameChange.bind(this)}/>
            </div>
            <div className="field">
            <label className="label">Ingredients</label>
            <div className="control has-icons-left has-icons-right">
              <input className="input" type="text" placeholder="Enter ingredients. (if more than 1 ingredient, separate them with commas)" ref="ingredients" value={this.state.revisedRecipe.ingredients} onChange={this.handleIndChange.bind(this)}/>
              <span className="icon is-small is-left">
                <i className="fa fa-flask"></i>
              </span>
            </div>
            </div>
            <div className="field is-grouped">
              <div className="control">
                <button className="button is-primary" onClick={this.closeModal}>Edit Recipe</button>
              </div>
              <div className="control">
                <button className="button" onClick={this.closeModal}>Cancel</button>
              </div>
            </div>
            </form>
        </div>
        </Modal>
      </div>
    );
  }
}

export default RecipeEdit;

1 个答案:

答案 0 :(得分:0)

我相信在更新列表后尝试重新渲染时实际上会出现错误。食谱中的成分属性是一个数组(如getRecipes()所示),但您将成分的新状态(在EditRecipe中)设置为字符串:"egg,flour,salt,water",然后尝试将成分呈现为一个数组:ingredients.map()

当您使用数组<input value={["egg", "flour"]} />呈现输入字段时,它确实显示以逗号分隔的值,但event.target.value中的onChange实际上是一个字符串。

在EditRecipe中,handleIndChange可以修复:

this.setState({revisedRecipe: {ingredients: e.target.value.split(',')}});

这确实存在另一个问题,但是你完全覆盖了revisedRecipe。因此,所有setState调用应该类似于:

const recipe = this.state.revisedRecipe;
recipe.ingredients = e.target.value.split(',');
this.setState({revisedRecipe: recipe);