Project 85 of 100 - Dynamic Restaurant Menu in React

Hey! I'm on a mission to make 100 React.js projects. Please follow my dev.to profile or my twitter for updates and feel free to reach out if you have questions. Thanks for your support!

Link to today's deployed app: Link
Link to the repo: github

This is part 5 of 15 in a series on building React projects by John Smilga, covered in this 10 hour video on Youtube.

In this little tutorial we built on the skills developed in previous videos in terms of importing data from an external file in the JSON format and iterating over those objects to display components. Those objects contain data for different items a restaurant might have on the menu, and take the following form in this project:

{
    id: 1,
    title: 'buttermilk pancakes',
    category: 'breakfast',
    price: 15.99,
    img: './images/item-1.jpeg',
    desc: `I'm baby woke mlkshk wolf bitters live-edge blue bottle, hammock freegan copper mug whatever cold-pressed `,
  }

The main new skill we learned in this little video was how we might go about filtering the data to dynamically display components based on a user selection. We also dynamically display buttons for each category so that if a new category was listed later, users could see that too.

The first way John creates the buttons for choosing a category on the menu is totally manual. We hard-code the buttons and then filter the items shown on the page based on which button was pressed. But this doesn't work for dynamic data, where this month you might have lunch specials (for example) and next month you don't.

To dynamically show buttons for each category listed in the menu data, in our App component we first map over the items in the menu data to return an array of just the categories.

import items from './data';

items.map(item => item.category)

Then we use the native Javascript Set constructor to create a set, which essentially just takes in an array and returns a new array with any duplicates removed (this is good to know ;) ).

const allCategories = new Set(items.map(item => item.category))

The last thing we do is destructure the array returned by the Set constructor because we need to also add in the 'All' category which we want to include in the categories that users can press. It's sort of the default. We do that thusly:

const allCategories = ['all',...new Set(items.map(item => item.category))];

Now in the App component we create a state variable called categories and initialize the component with the allCategories array we created above, outside of the component.

const [categories, setCategories] = useState(allCategories)

We then create a function in the App component outside of the return statement that filters the items shown given a string.

const filterItems = (category) => {
    if (category === 'all') {
      setMenuItems(items)
      return
    }
    const newItems = items.filter((item) => item.category === category)
    setMenuItems(newItems)
  }

You can now pass this function to the categories buttons and filter based on whatever string you want to pass in. To do this dynamically, simply build the buttons dynamically and pass the category to each button's onClick method. I do this in a Categories component:

const Categories = ({categories,filterItems}) => {
  return <div className='btn-container'>
    {categories.map((category,index) => {
      return <button 
        type='button' 
        className='filter-btn' 
        key={index}
        onClick={() => filterItems(category)}
        >
        {category}
      </button>
    })}
  </div>;
};

Now any time a new menu item is added that might have a new category, your buttons will already have this functionality built in.

If you like projects like this and want to stay up to date with more, check out my Twitter @jwhubert91, I follow back! See you tomorrow for another project.

15