We're going to build a simplified food delivery menu page. It'll have a list of dishes plus a form to filter them. The final result should look something like this:
Don't worry, we'll work our way there step by step.
- Clone this repo
- Run
npm install
- Run
npm start
to open the site
Open src/index.js
in your editor. This is where we render our React app to the DOM. You can see that we have a top-level component named App
. Open App.js
to see what's being rendered.
React can render arrays of elements just like a single element. E.g.
function App() {
return (
<ul>
<li>Apple</li>
<li>Orange</li>
<li>Banana</li>
</ul>
);
}
is the same as:
function App() {
return <ul>{[<li>Apple</li>, <li>Orange</li>, <li>Banana</li>]}</ul>;
}
This isn't very ergonomic, but it comes in very handy when you need to render a dynamic list. We can generate an array from our data and render that:
function App() {
const fruits = ["Apple", "Orange", "Banana"];
const fruitList = fruits.map(fruit => <li key={fruit}>{fruit}</li>);
return <ul>{fruitList}</ul>;
}
We're passing a special prop called key
to the top-level element in our array. This allows React to keep track of where each element is so it doesn't mess up the order. key
should be unique and not change when the array order does. React will warn you if you forget this.
It's common to inline the .map()
call:
function App() {
const fruits = ["Apple", "Orange", "Banana"];
return (
<ul>
{fruits.map(fruit => (
<li key={fruit}>{fruit}</li>
))}
</ul>
);
}
Uncomment the line importing "../data.js"
. This is an array of objects, each representing a dish in our restaurant. Use .map
to render all of them to the page inside the ul
.
Take a look at what data you have available for each dish and try to render it all. You should end up with something like this:
We want to be able to filter the list of dishes by minimum and maximum price. To do this we'll need to create two range inputs. Create some state to represent the range values.
You can refer back to the introduction to controlled components if you need to.
It can be a good idea to group and label related elements using the fieldset element.
You should end up with something like this:
Now we need to filter our dish list based on the price state.
Hint (you can click me)
Remember our list is a normal JavaScript array. You can manipulate it using any of the array methods you're used to.
You should have something like this:
Our App
component is starting to get a bit unwieldy. We've got our state management plus two totally separate bits of the page all rendering in one place. Let's try splitting it up into a couple of smaller components.
Create two new files: DishList.js
and PriceFilter.js
. Create new components in each and copy over the respective code from App.js
. You won't have access to the state you were using in the new files—you'll need to find a way to pass data down to child components from App
.
Don't forget to export your new components!
Hint (you can click me)
You might want to review the section on props from the earlier workshop.
We also want to filter our dishes by category. This is a good use-case for a group of radio inputs as the categories are mutually exclusive.
Create a new file called CategoryFilter.js
and make a new component in it. We need a radio input for each category. You can probably find a way to render the inputs dynamically so you don't have to write 8 radios by hand :)
You'll also need a state value to keep track of which radio is selected. You should end up with something like this:
Now we need to filter our list by category as well as the existing price filters. Use your category state value to filter the array in DishList
. Make sure you keep the price filter working.
If everything is hooked up correctly you should see something like the gif from the beginning 🎉
- It would be nice if the price filters were constrained by the lowest/highest available price on the page (so you don't end up filtering everything away)
- Add a text input that lets users search for dishes by title
- Make it look even better 💅