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>
)
}
}