I made a recipe box with ReactJS. Please let me know what room for improvement you see. I did notice that with codepen, the list of components started to become lengthy to look at. Is it a best practice to separate the different components into different js files? Here is the codepen: https://codepen.io/capozzic1/pen/gmpVyr. Here is the code:
var Grid = ReactBootstrap.Grid;
var Col = ReactBootstrap.Col;
var Row = ReactBootstrap.Row;
/*
Component ideas
-recipeCon
-RecipeList
-Recipe ingredients
-edit-delete
-addRecipe button
-recipe entry
*/
var recipes = [{
title: "Pizza",
ingredients: ["Tomato Sauce", "Yeast"]
}, {
title: "Cookies",
ingredients: ["Cookie Dough", "Chocolate chips"]
}, {
title: "Turkey tacos",
ingredients: ["Nacho cheese taco shells", "Turkey"]
}
]
//holds all other components. has add/edit methods
class RecipeContainer extends React.Component {
constructor(props) {
super(props);
this.state = ({
recipes: recipes,
edit: false,
editTitle: "",
editIngred: "",
});
this.addRecipe = this.addRecipe.bind(this);
this.editRecipe = this.editRecipe.bind(this);
this.addBtnEdit = this.addBtnEdit.bind(this);
this.editText = this.editText.bind(this);
this.editContents = this.editContents.bind(this);
this.handleOpen = this.handleOpen.bind(this);
}
editRecipe() {
this.setState({
edit: true
});
}
editText(editTitle, editIngred) {
this.setState({
editTitle: editTitle,
editIngred: editIngred
})
}
addRecipe(recipe) {
recipes.push(recipe);
this.setState({
recipes: recipes
});
}
addBtnEdit() {
this.setState({
edit: false
});
}
editContents() {
this.setState({
recipes: recipes
});
console.log(recipes);
}
handleOpen() {
document.querySelector(".recEntry").style.visibility = 'visible';
}
render() {
return (
<Grid>
<Row className="show-grid">
<Col md={12}>
<div className="recipeCon">
<h2 className="text-center mainh2">A recipe box</h2>
<RecipeList recipes={this.state.recipes}
edit={this.editRecipe}
editText={this.editText}
deleteContents={this.editContents}
open={this.handleOpen}/>
<AddRecipeBtn editTrue={this.addBtnEdit}
open={this.handleOpen}/>
</div>
<div className="recEntryWrap">
<RecipeEntry addRecipe={this.addRecipe}
editStat={this.state.edit}
editTitle={this.state.editTitle}
editIngred={this.state.editIngred}
editCont={this.editContents}/>
</div>
</Col>
</Row>
</Grid>
);
}
}
//show list of recipe names
class RecipeList extends React.Component {
constructor(props) {
super(props);
this.handleEdit = this.handleEdit.bind(this);
this.handleDelete = this.handleDelete.bind(this);
this.handleToggle = this.handleToggle.bind(this);
}
handleEdit(e) {
this.props.edit();
var editTitle = $(e.target).siblings("h2").text();
var editIngred = $(e.target).siblings("li").map(function(i, listi) {
return $(listi).text();
}).toArray().join(',');
this.props.editText(editTitle, editIngred);
//document.appendChild(test);
//value={editStat ? editTitle : ""}
$('.titleSpan').text(editTitle);
$('.inpIngred').val(editIngred);
this.props.open();
}
handleDelete(e) {
var recTitle = $(e.target).siblings("h2").text();
for (var i = 0; i < recipes.length; i++) {
if (recTitle === recipes[i].title) {
recipes.splice(i, 1);
}
}
this.props.deleteContents();
}
handleToggle(e) {
var sib = $(e.target).siblings().toggle();
console.log(sib);
}
//onclick of edit, get title and ingredients
render() {
//generate list of li items and add to ul
var recipes = this.props.recipes;
var recLi = recipes.map((recipe, index) =>
<div key={index}>
<h2 className="rech2" onClick={this.handleToggle}>{recipe.title}</h2>
{
recipe.ingredients.map((ingred,index) =><li key={index + "id"}> {ingred}</li>)
}
<button onClick={this.handleEdit} className="editBtn">Edit</button>
<button onClick={this.handleDelete} className="deleteBtn">Delete</button>
</div>
);
return (
<div className="recList">
<ul className="recUl">
{recLi}
</ul>
</div>
);
}
}
//recipe button for showing recipe entry div
class AddRecipeBtn extends React.Component {
constructor(props) {
super(props);
this.showEntry = this.showEntry.bind(this);
}
showEntry() {
this.props.open();
this.props.editTrue();
//empty the inputs and spans whenever add new recipe is clicked
$(".inpIngred").val("");
$(".inpName").val("");
$(".titleSpan").text("");
}
render() {
return (
<button onClick={this.showEntry} className="bigAdd">Add New Recipe</button>
);
}
}
//enter a new recipe, also handles edit of recipes
class RecipeEntry extends React.Component {
constructor(props) {
super(props);
this.handleAdd = this.handleAdd.bind(this);
this.handleEdit = this.handleEdit.bind(this);
this.handleClose = this.handleClose.bind(this);
}
handleAdd() {
var titleVal = document.querySelector(".inpName").value;
var ingVal = document.querySelector(".inpIngred").value;
var newRecipe;
var ingArr = ingVal.split(",");
for (var i = 0; i <= recipes.length; i++) {
if (recipes.length >= 0) {
newRecipe = {
title: titleVal,
ingredients: ingArr
};
this.props.addRecipe(newRecipe);
this.handleClose();
break;
} else if (titleVal === recipes[i].title) {
alert("This recipe already exists");
break;
}
}
}
handleClose() {
var recEntry = document.querySelector(".recEntry");
recEntry.style.visibility = "hidden";
}
handleEdit() {
var titleVal = document.querySelector(".titleSpan").textContent;
var ingVal = document.querySelector(".inpIngred").value;
var ingArr = ingVal.split(",");
console.log(titleVal);
//update specific object for recipes array
for (var i = 0; i < recipes.length; i++) {
if (recipes[i].title == titleVal) {
var newTitle = document.querySelector(".inpName").value;
recipes[i].title = newTitle;
recipes[i].ingredients = ingArr;
}
}
console.log(recipes);
//let the recipe container know to update the state
this.props.editCont();
this.handleClose();
}
render() {
var editStat = this.props.editStat;
var editTitle = this.props.editTitle;
var editIng = this.props.editIngred;
return (
<div className="recEntry">
<h2>{editStat ? 'Edit a recipe' : 'Add a recipe'}</h2>
<h3>Title</h3>
<p><b>{editStat ? 'Old title: ' : ""}</b><span className="titleSpan"></span></p>
<input type="text" className="inpName" />
<h3>Ingredients</h3>
<p className="instruct">Please put commas(",") in between ingredient names.</p>
<textarea className="inpIngred" />
<button onClick={editStat ? this.handleEdit : this.handleAdd} className="saveBtn">{editStat ? 'Save' : 'Add recipe'}</button>
<button onClick={this.handleClose} className="closeBtn">Close</button>
</div>
);
}
}
ReactDOM.render(<RecipeContainer/>, document.querySelector(".recWrap"));
/*$(".rech2").on('click',function(){
console.log("test");
});*/