Rafael Menezes

First App - First Components

In the last post, we created the basic structure for our first app. Let’s now define how the app suppose to work.

Managing our food intake means paying attention to the amount of calories we ingest. Every packed food you buy at the supermarket comes with a nutrition label in the back, on it we can find what we need, the amount of carbs, protein and fat per 100g.

To find the amount of calories the food contains, we just have to do a simple calculation:(carbs + protein) * 4 + fat * 9

So for example, in the nutrition label bellow we notice that this food contains 2.8g of protein, 12.4g of carbs and 4.9g of fat per 100g.

Applying the formula above – (12.4 + 2.8) * 4 + 4.9 * 9 – we now know that a 100g of this food gives us 104.9 calories.

So now we need to log the food we eat. We need to log the name, the date we had it, the size of the serving and the unit of measure. For example: 100g of beef, 100ml of milk, 2 slices of bread, 1 egg, etc… And, we need to log the amount of carbs, protein, fat and calories this serving has. Maybe you’re asking “Do we need to define the amount of calories? Isn’t it calculated?” Well, yes it is, but sometimes we don’t really know all the information, only the calories. Also, alcoholic drinks like vodka or scotch have 0 carbs, protein and fat but still have around 200 calories in 100ml. And we still want to be able to log it right?

In Javascript terms, our object would be like this:

{
  "name": "Food name",
  "serving": "150",
  "unit": "g",
  "date": "2019-01-24",
  "carbs": 18.6,
  "protein": 4.2,
  "fat": 7.4,
  "calories": 157.8
}

Now that we have defined what we need, let’s write our first component. Inside the src folder, create another one called components, this is a common practice to keep the code organized. Inside the components folder, create a file called App.jsx, and insert the following code:

import React from 'react'

class App extends React.Component {
  render() {
    return <p>App Component</p>
  }
}

export default App

To use this component go to the index.js file, import the App component and replace the h1 with the App tag:

import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App'

ReactDOM.render(<App />, document.getElementById('main'))

Now, if you go to the browser you will see “App Component”. Our main component is ready but is quite boring, let’s add a menu so in the future we can navigate through the pages. Create a new file called Header.jsx and add the following code:

import React from 'react'

const Header = () => {
  return (
    <nav className="navbar navbar-expand-lg navbar-dark bg-dark">
      <a className="navbar-brand" href="#">
        Food Logger
      </a>
      <div className="collapse navbar-collapse" id="navbarNav">
        <ul className="navbar-nav" />
      </div>
    </nav>
  )
}

export default Header

Different from the App component, Header is not defined as a class, it is only a function that returns a JSX tag. This is known as a stateless component and it’s mostly used when you have a component that doesn’t have any functionality, only the render method.

Now import the Header (line 3) in the App component and add the specific tag for it (line 9):

import React from 'react'
import 'bootstrap-css-only'
import Header from './Header'
class App extends React.Component {
  render() {
    return (
      <React.Fragment>        <Header />
        <div className="container">          <h2>App Component</h2>        </div>      </React.Fragment>    )
  }
}

export default App

In JSX you can’t return more than one tag. This tag can have multiple children but it can’t have siblings. With React.Fragment we can group and return multiple tags without adding anything extra to the DOM. You should see now the navbar and “App Component” in the screen.

In the next step we will display a food entry so we can have an idea of how it will look like. Create a new file inside the components folder called Food.jsx and add the following code:

import React from 'react'

class Food extends React.Component {
  render() {
    const { carbs, protein, fat, calories } = this.props.food
    const total = carbs + protein + fat
    return (
      <div className="fl-food mt-2 mb-4">
        <h5>
          {this.props.food.name}
          <small className="text-muted">
            {' – '}
            {this.props.food.serving} {this.props.food.unit}
          </small>
        </h5>
        <h6>
          Calories: <strong>{calories}</strong>
        </h6>
        <span>
          <small className="mr-4">
            <strong>Carbs:</strong> {carbs}
          </small>
          <small className="mr-4">
            <strong>Protein:</strong> {protein}
          </small>
          <small className="mr-4">
            <strong>Fat:</strong> {fat}
          </small>
        </span>
        <div className="progress" style={{ height: '10px' }}>
          <div
            className="progress-bar bg-danger"
            style={{ width: Math.round((carbs / total) * 100) + '%' }}
            title={'Carbs ' + carbs}
          />
          <div
            className="progress-bar bg-success"
            style={{ width: Math.round((protein / total) * 100) + '%' }}
            title={'Protein ' + protein}
          />
          <div
            className="progress-bar bg-warning"
            style={{ width: Math.round((fat / total) * 100) + '%' }}
            title={'Fat ' + fat}
          />
        </div>
      </div>
    )
  }
}

export default Food

No we have to change the App component to be able to view the information:

import React from 'react'
import 'bootstrap-css-only'
import Header from './Header'
import Food from './Food'
class App extends React.Component {
  render() {
    const food = {      name: 'Food name',      serving: '150',      unit: 'g',      date: '2019-01-24',      carbs: 18.6,      protein: 4.2,      fat: 7.4,      calories: 157.8,    }    return (
      <React.Fragment>
        <Header />

        <div className="container">
          <Food food={food} />        </div>
      </React.Fragment>
    )
  }
}

export default App

All done! You can see the sample food in the browser with a progress bar so we can quickly identify understand the information. One last step, let’s create a list of food and reuse the Food component to display all of them. Create a new file in the src folder called foodData.js:

const foodNames = [
  'Beef Mince',
  'Unsweetened chocolate',
  'Green beans',
  'Marshmallows',
  'Wasabi',
  'Raspberries',
  'Chicken',
  'Bacon',
  'Crayfish',
  'Clams',
  'Blueberries',
  'Bread',
  'Papayas',
  'Apricots',
  'Red beans',
  'Mango',
  'Chai',
  'Pine nuts',
  'Feta cheese',
  'Gelatin',
]

const random = (num = 0) => Math.floor(Math.random() * num)

const generate = (qty = 5) => {
  const foodData = {}
  for (let i = 0; i < qty; i++) {
    const id = 'food' + i

    let carbs = random(70),
      protein = random(50),
      fat = random(30)

    const food = {
      name: foodNames[random(20) + 1],
      serving: random(200),
      unit: 'g',
      carbs: carbs,
      protein: protein,
      fat: fat,
      calories: (carbs + protein) * 4 + fat * 9,
    }
    foodData[id] = food
  }
  return foodData
}

export default generate

It exports the function generate that create and return a list of random food. Now, we have to import the the foodData file in the App component (line 3), set the generated data to the component state (line 9) and loop through it (line 18-20):

import React from 'react'
import 'bootstrap-css-only'
import generate from '../food-data'import Header from './Header'
import Food from './Food'

class App extends React.Component {
  state = {    consumed: generate(),  }
  render() {
    return (
      <React.Fragment>
        <Header />

        <div className="container">
          {Object.keys(this.state.consumed).map(key => (            <Food key={key} food={this.state.consumed[key]} />          ))}        </div>
      </React.Fragment>
    )
  }
}

export default App