The last piece of functionality we miss is the ability to filter bills by category.

We want to click a category name in the NavBar component, and the table and chart should only display that category’s bills.

And we’ll add an ‘All’ button that is the default and will let us go back to show all the bills instead of filtering by one category.

Let’s do it!

I’m going to first restructure the App component a little bit, by:

  • adding an activeCategory property to the state
  • creating an activeBills computed property that will only return the bills associated with the currently selected categories (or all if none is selected)
  • passing activeBills to the BillsTable and Chart components, instead of bills so they will show the filtered bills.

The first change is easy, in the App component:

const [activeCategory, setActiveCategory] = useState()

Let’s create the activeBills function. We filter the bills by activeCategory, and we sort them by date in reverse chronological order (newest first):

const activeBills = () => {
  return bills
    .filter(bill =>
      activeCategory ? bill.category === activeCategory : true
    )
    .sort((a, b) => (new Date(a.date) < new Date(b.date) ? 1 : -1))
}

Now we pass that to the child components, instead of bills:

<div className="container flex">
  <div className="w-1/2">
    <BillsTable
      bills={activeBills()}
      showAddBill={showAddBill}
      removeBill={removeBill}
    />
  </div>
  <div className="w-1/2">
    <Chart bills={activeBills()} />
  </div>
</div>

Now we’re ready to go in the NavBar component and do our edits there. But first let’s add 2 new props to NavBar: activeCategory and setNewActiveCategory

<NavBar
  categories={categories}
  showAddCategory={showAddCategory}
  activeCategory={activeCategory}
  setNewActiveCategory={setNewActiveCategory}
/>

setNewActiveCategory points to this function which simply calls setActiveCategory passing the category index:

const setNewActiveCategory = index => {
  setActiveCategory(index)
}

Let’s get to the NavBar component. Here’s the NavBar component at the moment, before any change:

import React from 'react'

export default props => {
  const triggerShowAddCategory = () => {
    props.showAddCategory()
  }

  const liStyle =
    'p-4 inline bg-grey-lighter hover:bg-grey-light uppercase font-black cursor-pointer'

  return (
    <ul className="list-reset inline flex justify-center border-b-4 mb-0">
      {props.categories
        ? props.categories.map((value, index) => {
            return (
              <li className={liStyle} key={index}>
                {value}
              </li>
            )
          })
        : '<li>No categories</li>'}
      <li className={liStyle} onClick={triggerShowAddCategory}>
        ➕
      </li>
    </ul>
  )
}

Let’s add a setNewActiveCategory() function we’ll use to call the setNewActiveCategory() prop passed by the App component:

const setNewActiveCategory = index => {
  props.setNewActiveCategory(index)
}

We’ll use that inside the categories map() to apply the class bg-grey-dark if a category is active. This class is part of Tailwind and will apply a darker background. The liStyle variable is just a string, so we concatenate a new class using +.

Also, when we click a category we call the setActiveCategory method, passing the category name:

{props.categories.map((value, index) => {
  return (
    <li
      className={
        liStyle +
        (props.activeCategory === index
          ? ' bg-grey-dark'
          : ' bg-grey-lighter')
      }
      key={index}
      onClick={() => setNewActiveCategory(index)}
    >
      {value}
    </li>
  )
})}

Before iterating on categories, let’s add an “All” button. Clciking it will reset the active category:

<li
  className={
    liStyle +
    (props.activeCategory === '' || props.activeCategory === undefined
      ? ' bg-grey-dark'
      : ' bg-grey-lighter')
  }
  onClick={() => setNewActiveCategory('')}
>
  All
</li>

Now whenever we click a category, we ask App to update the activeCategory state property, and that’s automatically passed down again to the NavBar component to let it highlight the category, and React is also updating the active bills, so BillsTable and Chart components only show the active category amounts.

See the app in the current state on CodeSandbox.


Go to the next lesson