Skip to content

Instantly share code, notes, and snippets.

@caprosset
Last active February 14, 2021 09:52
Show Gist options
  • Save caprosset/2942697f4fdb42e2f6d34135ef6b7ad1 to your computer and use it in GitHub Desktop.
Save caprosset/2942697f4fdb42e2f6d34135ef6b7ad1 to your computer and use it in GitHub Desktop.
LAB Solution | React IronNutrition
// src/components/AddNewFood.js
import React, { Component } from 'react';
class AddNewFood extends Component {
state = {
name: "",
calories: "",
image: ""
}
handleChange = e => {
let { name, value } = e.target;
this.setState({[name]: value})
}
handleSubmit = e => {
e.preventDefault();
// creamos un objeto newFood que contiene el state
const newFood = this.state;
// invocamos la función creada en FoodList.js pasándole el objeto comida que se acaba de crear
this.props.addFood(newFood);
// vacíamos el formulario
this.setState({
name: "",
calories: "",
image: ""
})
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<div className="field">
<label className="label">Name</label>
<div className="control">
<input
className="input"
type="text"
name="name"
value={this.state.name}
onChange={this.handleChange} />
</div>
</div>
<div className="field">
<label className="label">Calories</label>
<div className="control">
<input
className="input"
type="number"
name="calories"
value={this.state.calories}
onChange={this.handleChange} />
</div>
</div>
<div className="field">
<label className="label">Image</label>
<div className="control">
<input
className="input"
type="text"
name="image"
value={this.state.image}
onChange={this.handleChange} />
</div>
</div>
<div className="control">
<button type="submit" className="button is-link">
Submit new food
</button>
</div>
</form>
</div>
)
}
}
export default AddNewFood;
// src/App.css
.container {
display: flex;
}
.container .section {
width: 50%;
}
#add-section {
margin: 30px 0;
}
#todays-food-title {
font-size: 24px;
font-weight: bold;
}
li {
display: flex;
align-items: center;
}
// src/App.js
import React from 'react';
import './App.css';
import FoodList from './components/FoodList';
const App = () => {
return (
<div className="App">
<FoodList />
</div>
)
}
export default App;
// src/components/FoodBox.js
import React, {Component} from 'react';
import './../App.css';
class FoodBox extends Component {
state = {
quantity: 1
}
handleChange = e => {
const { value } = e.target;
this.setState({ quantity: Number(value) })
}
render() {
const { name, calories, image } = this.props.food;
return (
<div className="box">
<article className="media">
<div className="media-left">
<figure className="image is-64x64">
<img src={image} width="200" height="150" />
</figure>
</div>
<div className="media-content">
<div className="content">
<p>
<strong>{name}</strong> <br />
<small>{calories} cal</small>
</p>
</div>
</div>
<div className="media-right">
<div className="field has-addons">
<div className="control">
<input
className="input"
type="number"
value={this.state.quantity}
onChange={this.handleChange}
/>
</div>
<div className="control">
<button
onClick={() => this.props.updateTodaysFood({...this.props.food, quantity: this.state.quantity})}
className="button is-info">
+
</button>
</div>
</div>
</div>
</article>
</div>
)
}
}
export default FoodBox;
// src/components/FoodList.js
import React, { Component } from 'react';
import shortid from 'shortid';
import foods from './../foods.json';
import FoodBox from './FoodBox';
import AddNewFood from './AddNewFood';
import Search from './Search';
import TodaysFood from './TodaysFood';
class FoodList extends Component {
state = {
foodsList: foods,
filteredFoodsList: foods,
showForm: false,
todaysFood: [],
totalCalories: 0
}
// ITERACIÓN 3 - ADD NEW FOOD
toggleForm = () => {
// a cada clic del usuario, el estado de showForm cambiará por su contrario (si el formulario está escondido se mostrará, y vice-versa)
this.setState({showForm: !this.state.showForm});
}
// ITERACIÓN 3 - ADD NEW FOOD
addFood = (newFoodObj) => {
// hacer una copia de la lista original de comidas
const foodsListCopy = [...this.state.foodsList];
// añadir la nueva comida en 1era posición del array
foodsListCopy.unshift(newFoodObj);
// resetear el state
this.setState({filteredFoodsList: foodsListCopy});
// una vez la comida está añadida a la lista, esconder el formulario
this.state.showForm = false;
}
// ITERACIÓN 4 - SEARCH BAR
filterFoods = (searchTerm) => {
// convertimos la palabra buscada en minúsculas
const searchedTerm = searchTerm.toLowerCase();
// filtramos una copia de la lista original de comidas para devolver únicamente las comidas cuyo nombre (en minúsculas también) corresponden al término de búsqueda
const filteredList = [...this.state.foodsList].filter( foodObj => {
return foodObj.name.toLowerCase().includes(searchedTerm);
})
// actualizamos la lista de comidas filtradas (la que vamos a mostrar en el render())
this.setState({filteredFoodsList: filteredList})
}
// ITERACIÓN 5 Y 6 (BONUS)
addTodaysFood = (foodObj) => {
// creamos una copia del array de las comidas de hoy
let todaysFoodsCopy = [...this.state.todaysFood]
// comprobamos si la comida a añadir ya se encuentra en el array todaysFoodsCopy
let found = todaysFoodsCopy.find(food => food.name === foodObj.name);
// calculamos las calorías totales según qué cantidad de la comida se ha añadido
foodObj.calories *= foodObj.quantity;
if(found) {
// si la comida ya existe en el array de comidas de hoy, le sumamos al objeto comida la cantidad y calorías que se añadieron nuevamente
found.quantity += foodObj.quantity;
found.calories += foodObj.calories;
} else {
// sino, añadimos el objeto foodObj al array todaysFoodsCopy
todaysFoodsCopy.push(foodObj);
}
// calculamos el total de calorías de todas las comidas de hoy
const totalCalories = todaysFoodsCopy.reduce(
(acc, val) => acc + val.calories, 0
);
// actualizamos el state con la lista actualizada de comidas de hoy y el nuevo total de calorías
this.setState({ todaysFood: todaysFoodsCopy, totalCalories });
}
// ITERACIÓN 7 (BONUS)
deleteFood (index) {
// creamos una copia del array de las comidas de hoy
const todaysFoodsCopy = [...this.state.todaysFood]
// sacamos la comida que corresponde al index recibido del array (copia) de comidas
todaysFoodsCopy.splice(index, 1);
// volvemos a calcular el total de calorías
const totalCalories = todaysFoodsCopy.reduce(
(acc, val) => acc + val.calories, 0
);
// actualizamos la lista de comidas con el array copia y el nuevo total de calorías después de eliminar la comida
this.setState({ todaysFood: todaysFoodsCopy, totalCalories });
}
render() {
return (
<div className="container">
<div className="section">
<h1 className="title">IronNutrition</h1>
<Search searchFoods={this.filterFoods} />
<div id="add-section">
<button className="button" onClick={this.toggleForm}>Add new food</button>
{ this.state.showForm
? <AddNewFood addFood={this.addFood} />
: null
}
</div>
<div className="columns">
<div className="column">
{this.state.filteredFoodsList.map( (food, index) => {
food.id = shortid.generate();
return (
<FoodBox
key={index}
food={food}
updateTodaysFood={this.addTodaysFood}
/>
)
})}
</div>
</div>
</div>
<div className="section">
<h2 id="todays-food-title">Today's foods</h2>
{/* comprobamos que el array todaysFood contiene algo
> si es el caso, mostramos lo que hay en el array todaysFood
> sino, no mostramos nada (null) */}
{this.state.todaysFood.length > 0
?
(<div>
<ul>
{this.state.todaysFood.map( (oneTodaysFood, index) => {
oneTodaysFood.id = shortid.generate();
return <TodaysFood
key={oneTodaysFood.id}
{...oneTodaysFood}
deleteFood={() => this.deleteFood(index)}
/>
})}
</ul>
<p>Total calories: {this.state.totalCalories}</p>
</div>)
: null
}
</div>
</div>
);
}
}
export default FoodList;
// src/components/Search.js
import React, { Component } from 'react';
class Search extends Component {
state = {
search: ""
}
handleChange = e => {
const { name, value } = e.target;
this.setState({[name]: value})
// invocar la función creada en FoodList.js que filtra la lista de comidas según la búsqueda
this.props.searchFoods(value);
}
render() {
return (
<div>
<input
type="text"
className="input search-bar"
name="search"
placeholder="Search"
value={this.state.search}
onChange={this.handleChange}
/>
</div>
)
}
}
export default Search;
// src/components/TodaysFood.js
import React from 'react'
const TodaysFood = (props) => {
const { name, calories, quantity, deleteFood } = props;
return (
<li>
<p>{quantity} {name} = {calories} cal</p>
<button className="button is-link" onClick={deleteFood}>Delete</button>
</li>
)
}
export default TodaysFood;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment