0

I am building a React CRUD app in which I can add a new dish, display the list of the dishes, update the dish, or delete a dish.

I'm stuck on the Update part in which I update the dish. I was able to code the dish title to update every time I edit the dish title. However, I wasn't able to edit the dish ingredients. I had the ingredients coded to convert the string from textarea to array. I need to do that because I added a filter function in which one type in an ingredient and you'll see possible dishes with that ingredient typed in.

I was able to convert the string to array in "Add Dish" (<AddDish />) component. Then I display the array in the {this.showFood()} in which I use .join(", ") to convert the array to string.

However, the part I'm struggling with is updateDish() function. If I edit the title without editing the ingredients, I'm able to display everything. However, if I edit the ingredients and click "Save" button, I get an error "this.state.ingredients.join is not a function".

I discovered that every time I tried to edit the ingredients, it returns a string. I tried to edit the code to check if the ingredients return a string, to convert the string to an array and I can see that it updated the food-list.json which I used as a database but I'm still getting this error. Please help...

You can check out everything at this demo CodeSandBox: https://codesandbox.io/s/github/kikidesignnet/food-list/tree/master/

App.js

import React, { Component } from "react";
import DishBox from "./components/DishBox/index";
import AddDish from "./components/AddDish/index";
import SearchBox from "./components/SearchBox/index";
import './App.css';
import fooddb from "../src/food-list.json";
import 'bootstrap/dist/css/bootstrap.min.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      list: fooddb,
      filtered: fooddb,
      searchInput : ""
    }
  }

  searchChange = (filterText) => {
    this.setState({
      searchInput: filterText
    });
  }


  addDish = (newDish) => {
    console.log("newdish", newDish);
    newDish.ingredients = newDish.ingredients.split(", ");
    const dishsCopy = [...this.state.list];
    dishsCopy.push(newDish);
    this.setState({
      list: dishsCopy,
      filtered: dishsCopy
    })
  }

  showFood = () =>{
    let currentArr = [...this.state.filtered];

    const filter = this.state.searchInput.toLowerCase();
    
    if(filter !== "") {
       currentArr = currentArr.filter((d) => {
        let lc = d.ingredients.map((ing) => ing.toLowerCase());
        return lc.includes(filter)
      })
    }

    return currentArr.map((eachFood, index) => {
        return(
          <DishBox
          key={index}
          id={index}
          dish={eachFood.food}
          ingredients={eachFood.ingredients}
          updateDish={this.updateDish}
          clickToDelete={this.deleteDish.bind(index)}
          />
        );
    });
  }

  updateDish = (i, food, ingredients) => {

    console.log("updatedIng", typeof ingredients);

    const filteredCopy = [...this.state.filtered];
    filteredCopy[i].food = food;

    if(typeof ingredients === "string") {
      filteredCopy[i].ingredients = ingredients.split(",");
    } else {
      filteredCopy[i].ingredients = ingredients;
    }
    
    this.setState({
      list: filteredCopy,
      filtered: filteredCopy
    });

  }


  deleteDish = (dishIndex) => {
    const dishsCopy = [...this.state.list];
    dishsCopy.splice(dishIndex, 1);
    this.setState({
      list: dishsCopy,
      filtered: dishsCopy
    })
  }
 
  render() {
    console.log("json db", this.state.filtered);
    return (
      <div className="App">
        <header className="App-header">
        <h1>Grand Food Tour</h1>
        </header>
        <div className="food-section">
          <div className="container">
          <SearchBox searchInput={this.state.searchInput} searchChange={this.searchChange}/>
          <div className="food-list">
            {this.showFood()}
          </div>
          <AddDish addDish={this.addDish} />
            </div>
        </div>
      </div>
    );
  }
}

export default App;

DishBox.js

import React, { Component } from 'react';

export default class DishBox extends Component {
  constructor(props) {
    super(props);
    this.state = {
      food: this.props.dish,
      ingredients: this.props.ingredients,
      indexNum: this.props.id,
      isEditing: false
    }
    this.handleUpdate = this.handleUpdate.bind(this);
    this.pressEditBtn = this.pressEditBtn.bind(this);
    this.cancel = this.cancel.bind(this);
  }

  onFoodChange = (event) => {
    event.preventDefault();
    console.log("foodchange", event.target.value);
    this.setState({ food: event.target.value });
  }

  onIngChange = (event) => {
    event.preventDefault();
    console.log("ingchange", event.target.value);
    this.setState({ ingredients: event.target.value});
  }

  pressEditBtn = () => {
    this.setState(state => ({ isEditing: !state.isEditing }));
  }

  cancel = () => {
    this.setState(state => ({ isEditing: !state.isEditing }));
  }

  handleUpdate = () => {
    // event.preventDefault();
    console.log("foodchange", this.state.food);
    console.log("ingchange", typeof this.state.ingredients);

    this.props.updateDish(this.state.indexNum, this.state.food, this.state.ingredients);
    this.setState(state => ({ isEditing: !state.isEditing }));
  }

  

  render() {
    const { isEditing, index } = this.state;
    return (

        <div className="dish-box">
        <div className="left-flex">
          <div className="food-title">
          {isEditing ? (<input type="text" name="food" value={this.state.food} onChange={event => this.onFoodChange(event, index)}  />) : (<h2>{this.props.dish}</h2>)}
          </div>
          {isEditing ? (<textarea name="ingredients" value={this.state.ingredients} onChange={event => this.onIngChange(event, index)} ></textarea>) : (<p>{this.state.ingredients.join(", ")}</p>)}
        </div>
        <div className="right-flex">
          {isEditing ? (<button type="button" className="btn btn-success" onClick={this.handleUpdate} >Save</button>) 
          : (<button type="button" className="btn btn-success" onClick={this.pressEditBtn} >Edit</button>)}
          {isEditing ? (<button type="button" className="btn btn-danger" onClick={this.cancel}>Cancel</button>) 
          : (<button type="button" className="btn btn-danger" onClick={this.props.clickToDelete}>Delete</button>)}
        </div>
      </div>
    )
  }
}

1 Answer 1

1

Change to:

  onIngChange = event => {
    event.preventDefault();
    console.log("ingchange", event.target.value);
    this.setState({ ingredients: event.target.value.split(",") }); // convert string to array
  };

The reason is that you set the state as event.target.value which is string.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.