Learn React.js by Building a Game Search App | Part 2/3 - Building The Search Bar and Fetching Data From The Backend
Since we have mostly finished our setup in the previous part of this tutorial (You can read part 1 here ). We’re ready to start working on the search page of the app which is the main page of the application. In that page, the user should be able to use the search bar to search for a given game title or franchise and the app should call our backend which will call the RAWG API to retrieve a list of relevant games. Then, the app should display those games as cards containing a cover, a title, genres and how long it takes to beat the game if that data is available. Now that you know what we need to build, let’s get started. I’ll introduce relevant concepts as we go. Subscribe to not miss out when future parts of the tutorial are released! How is React Initialized in Our Project? What are React Components? React VS React-DOM Making Our First React Component Creating The Search Component What Are Props in React? What Are Hooks in React? The useState Hook Setting Our Backend to Enable Search Requests to The RAWG API Setting Up Express Defining The /api/games Route Fetching Search Data on The Frontend What is The useEffect Hook and How it Works? How is Data Fetched With useEffect? Creating a Custom Hook If you look at your folder, you should have the following files. Before we write any code, let’s remove the folder and the file. We don’t need these two. Since we’re using Tailwind, the only place we need for CSS is the file. Now let’s take a look at . For React to be used in a webpage, it needs to inject the content of your app within your HTML. To be more precise, we first need to create a root within your file from which your React app will be rendered. If you take a look at your file at the root of your project’s folder. You will notice the presence of a with an id of “root”. This was added automatically by Vite. Looking back at our React code : We can see that to inject our React app within the having the id “root” we pass that as a param of the function. It then creates an object that has a method called . We immediately call it to pass our main React component called wrapped under StrictMode (which adds useful behaviors to more easily find bugs). The component is the main building block when working in React. It allows you to build UIs in a modular fashion allowing you to reuse components in multiple places. It also allows you to manage complexity by placing logic specific to a component within it. At a basic level, a React component is just a JavaScript function that returns some JSX. Which, if you remember, is syntax that looks like HTML that you can write within JavaScript in a .jsx file. Here is an example! We have a React component that returns a containing an tag with the content “Hello World!”. You will surely notice that we defined this component by defining a function with its name’s first letter capitalized. The convention when defining React components is to use the PascalCase notation rather than the usual camelCase. Another thing that makes components really useful in React is that you can place React components within React components, enabling a modular approach to building UIs. Here’s another example. As you can see, to place a component within another you can use the self closing tag notation you’re used to from HTML along with the name of that component. Looking back at the import statements we had in . You’ll notice that we’re importing things from both and . You might be wondering why they are separate entities? React is the library that allows us to build UIs using reusable blocks called components that return JSX. React-DOM on the other hand is the one taking care of rendering React components on the DOM (Document Object Model) of the webpage. That’s why is imported from and not since it’s used to render to the DOM with the method. The reason for this split is that React can be used outside of the web. For example, it can be used for mobile development with React Native. Instead of the JSX being translated to HTML elements that are rendered on a webpage, they’re are translated to native mobile UI elements instead. In addition to React being used for mobile UIs, it can also be used to make pdfs with react-pdf . react-pdf in action Making Our First React Component Now that you understand how components work, let’s define our first component, the component. If you take a look at again, you’ll notice that we’re already importing this component from a file called . Let’s start from scratch by deleting everything in the existing file and adding the following code which will display the app’s name and logo. The tag will contain all of the content of the search page. We define it as a container with a direction set to . We then center the content within that tag along the cross axis of the container using . Below is a schematic displaying how the axis are defined according to the direction of the flex container. Remember that using will center the content of the flex container along the cross axis while will center its content along the main axis. We then make the tag take the full available width of the page using and give it a padding of 2 with . All of the styling explained above is succinctly described in Tailwind by passing the needed class names to the className property of the JSX element. Now, you might be wondering why is it instead of that you’re accustomed to in HTML? The reason is that there is a difference in how the attribute is named in HTML vs the DOM which the underlying model representing a webpage. Since React operates on the DOM it was chosen to use instead which is what the DOM uses. Now within the tag, we define a which will hold the app’s name and logo. This is set as a flex container with the direction set to by default, so we don’t need to specify it. We center its content along the cross axis so that the app’s title and logo are near each other at the center. We then use an tag to display the logo. You might have noticed that the path to the image does not contain the folder despite the image being in that folder. That’s because Vite will automatically make whatever is in the folder as if it was available from the root of the project, so you do not need to specify the folder in the path. Finally and sets the size of the logo using units defined by Tailwind which are responsive. If you now start a development server using the command in your terminal, you should get the following result. Let’s makes things prettier before moving on. In add the following Tailwind classes to the body tag. We’re setting the background color to be of gray-950 which is a color predefined by Tailwind that is near black. We then set the background image and set it to repeat so that it tiles indefinitely making the image cover the page regardless of the size of the viewport. You should have the following result. As you can see the app’s name near the logo is not visible. To fix the issue, let’s add a nice looking gradient to it. To showcase how you can create custom classes composed of Tailwind classes, let’s add the following in our file. The key here is to use the directive and then use the Tailwind classes you need. Creates the gradient. Makes sure the gradient is applied within the text’s letters. If you go back to our React component and apply the custom Tailwind class we created along with a few other Tailwind classes, you should get the following result. Creating The Search Component In the previous section we created our main React component acting as the entrypoint of our UI. However, we need to define more than just one component in our app. To keep things organized, let’s create a folder under the folder. Within, let’s define our next component, the component by creating a file. Add the following code in . The component is essentially a search bar. You’ll probably notice that we have params that are passed to this component. These are called props. It’s another one of React’s major concepts. What makes components flexible in React is the ability to pass props to them to change how they’re rendered. This makes components usable in more than just one screen or one place in the app. You can view props the same way as params that you pass to a function. Here is a basic example of how props are used in React. To pass props to a component you can simply start adding arbitrary attributes to where you call this component. In the example above, I decided to use an attribute called “name” that is then available under the props object where the component is defined. That’s why I can do and get access to “Hello World”. You might have noticed that we use curly braces when putting prop.name within the tag. This is important, so that the actual value, “Hello World” is what’s rendered rather than having literally “prop.name” displayed. A more convenient way of dealing with props is to use destructuring which is a JavaScript concept allowing us to directly get the properties we need. So now, if I need more than one prop, I can do the following. Now that you understand what props are and how they work, let’s further analyze the code for the component. Most of the Tailwind classes here are easy to understand if you already know CSS so I will not go over each of them. I would like however, to explain a few that may not be as straightforward to figure out : : Applies 4 units to the bottom margin of the element. If you had instead, it would be applied for margin left, for margin right, etc… : Applies 2 units to both the top and bottom margin of the element. Therefore, along the y axis. If you where to use , the left and right margins would be increased instead since they’re along the x axis. Now let’s take a look at the input element. In React, the input attribute has two properties that are very useful. The attribute allowing us to set the value inside the field. The attribute which takes a function and will run it every time the user types in the field. Which also allows us the get what was typed. We pass to these two attributes the and props we defined for the component. Now, go back to and add the following code. What is this function we’re using? It’s called a hook and hooks are another one of React’s major concepts. Hooks let you use different React features from your components. You can either use the built-in hooks or combine them to build your own. Here in particular, we’re using a state hook to set a state variable with the goal of holding the user’s search query and to update the UI when it changes. From React’s docs, the usefulness of the hook is made apparent. To update a component with new data, two things need to happen: Retain the data between renders. Trigger React to render the component with new data (re-rendering). Creating a state variable using the useState hook allows us to achieve both. A state variable to retain the data between renders. A state setter function to update the variable and trigger React to render the component again. In our case, is the state variable and is the state setter function. Finally, we pass within the default state value we want to use. When creating a state variable here is the convention that is usually adopted. What makes React convenient for building UIs rather than using vanilla JS is that you don’t need to manual update the UI by changing the content of various HTML nodes. As we saw above, by using a state variable, React will take care of updating the UI automatically when the state variable changes. By passing an function that calls the state setter function and passing to it the value of what was typed using , we update the state variable. Since we’re also passing the state variable to the component, React will re-render the component everytime it’s changed. You should get the following result. We could have defined the state variable within the component but it’s better to do it in the component instead. This is a better approach because we need to have access to so that we can pass it to our backend and make the RAWG API call to get relevant search results. Now that the search bar is completed, let’s do some backend work to be able to query the RAWG API so it can provide the data we need. In your backend repo, add the following code to your file. We first import all the needed packages. I explained why we needed each of them in part 1 of this tutorial. Refer to it here . We then call to load our environment variables. We can then access them using We intialize the express web framework which is the core of our backend and pass it to a const called . We then set our CORS (stands for Cross Origin Resource Sharing) options which allows us to only authorized requests coming from the domain of our frontend. One thing to know about CORS is that it’s only enforced through the web browser. That means, a mobile app could still make requests to our backend despite the options we set. Learn more about CORS here . Using , we apply the CORS options to our Express app. Finally we set up our rate limiter preventing potential DDoS attacks where a client makes too many requests in order to take down our backend. To make our backend actually do something let’s add the following code. We define our first route that the client will be able to call and then make the Express app run by using . We want this route to return a list of games that matches the query passed by the client. In Express, a route is defined this way. Here we’re using a GET route since we’re not modifying a resource, we’re just requesting it. The first param is the route path we want to use, while the second param contains the function that’s going to run when the request is made. will contain the query of the client. Clientside, the query will be passed as a param to the route path. Here’s an example. Will result in containing . Now let’s continue by filling out the logic for the route. Let’s break it down. By using object destructuring we rename the param to . We do this because the RAWG API expect a search param and it would look weird to do : However, this is just a matter of personal preference. You can decide to stick with if you want. Within the Try/Catch statement we make the call to the RAWG API passing our API key from our environment variables. Something to note is that the RAWG API expects you to pass the API key as a param rather than putting it in the header like other APIs. Also, in Node.js you can’t use fetch by default as you can on the frontend. That’s why we had to install the node-fetch package. The rest of the code is pretty standard error handling. Either way, we always end up returning json with either an error message or the data the client requested. Finally, remember to run the backend using the following command : Now that we have created the backend route we needed, let’s head back to our frontend to write the logic allowing us to fetch the relevant data according to what query the user entered in the search bar. We need to first talk about another very important hook in React called . To put it simply, it’s a hook that lets you synchronize a component with an external system. In our case it’s going to be our backend. In practice, it’s a function that’s called within a component and takes usually two params. The first is the function you want to run which is your “effect” and the other is a dependency array. It will run after the component is first rendered and every time a dependency in its dependency array changes. Above is an example of how we use . We have an empty dependency array meaning that the effect will run only after the component renders the first time. This occurs once in prod but twice in development because we wrapped our app with . You can use without a dependency array but it will run after every render. In the example above, we fetch and display a new quote from a fictitious API when the user clicks on a button. You’ll notice that the dependency array of our has the state variable. This means that the hook will run once after the component is rendered the first time (twice in dev because of StrictMode) and then every time is modified. In practice, if you design your app in a way that you need the user to click a button to make an API call, you do not need to use as you could put the fetching logic within a function you pass to the button’s handler. The example above was just to demonstrate at a basic level how this hook works. In our app’s case, its usage is warranted since we need to make an API call when the user finishes typing something rather than clicking a button. While using this hook like shown above is ok for toy examples, in practice when dealing with APIs, I would recommend using a library like Tanstack Query formerly known as React Query which makes fetching, caching, synchronizing and updating server state a breeze. I recommend this part of the React docs regarding . Oftentimes, you might be tempted to use it while it’s not necessary. The docs provide helpful guidance regarding this hook. In our app, we will still use but also create our own hook to deal with loading states (what to show the user while we’re fetching data) and error handling. While React offers hooks like and , nothing prevents you from creating your own hook. This is what we’re going to do now. Our hook is going to be called and will be responsible for fetching the data we need from our backend and dealing with loading states and errors if they occur. Create a new folder called within the folder and within a file called . In the file you just created, add the following code : will expect a fetch function which will contain the API call and pass it to its own function and set the appropriate loading or error states depending ont the situation. The hook returns the data, the loading state, the error state, the fetchData function to fetch data again if needed and finally, a reset function to reset the state if needed. Let’s create the fetch function specific to getting the list of games related to a search query. For this purpose, let’s create file called within the folder. In it, add the following code. For the code above to work we need to define an environment variable in our frontend. This is done just to make it easy to swap in and out the domain of the backend as needed and not for security reasons. Create at the root of your project a file containing the domain of our backend. Now, we can go to where we will write the logic to fetch the needed data when the user enters a query in the search bar. Let’s break it down. We call and pass to it an anonymous function that will call our function created in the file and pass to it the user’s search query. The reason we’re wrapping it in a function is that we don’t want to call immediately but rather let call it in its function. We also couldn’t pass it on its own because we wouldn’t be able to pass the search query param along. Once called, the hook will return all that we need to display the relevant UI depending on the situation. Next, we have our . We set a timeout of 500ms as to provide enough time after the user has typed before making a request otherwise we would be making a call every time a letter changes which would be wasteful. This is called debouncing. Then, we have an if statement with a call to trim() that removes any whitespace at the beginning and end of a string. If the user didn’t type anything yet or just uses spaces in the search bar, an empty string will be returned which is falsy in JavaScript. Therefore, no API call will be made. Since will run after the component first renders, in that case the search bar would be empty, we wouldn’t want to make an API call after all. The function we pass to returns a function that clears the timeout we just created. This is called a cleanup function and is optional when using this hook but needed in our case. This is so we can avoid memory leaks. If we were to not use one here, every time the hook runs, we would create a new timer that would never be deleted. That’s about it. The logic for fetching data is done. To make sure that it actually works, let’s the data fetched. Below the we just added, create another one with the following content. (Yes, you can have more than one per component.) Make sure to re-run both the frontend with and the backend with . Now, if you try using the app. You should get the following result. In the last part of this tutorial, we will write the logic to display the search results in nice looking game card components and then work on displaying more details when the user clicks on a specific card. Subscribe for more interesting tutorials! UPDATE : Part 3 is now available! Since we have mostly finished our setup in the previous part of this tutorial (You can read part 1 here ). We’re ready to start working on the search page of the app which is the main page of the application. In that page, the user should be able to use the search bar to search for a given game title or franchise and the app should call our backend which will call the RAWG API to retrieve a list of relevant games. Then, the app should display those games as cards containing a cover, a title, genres and how long it takes to beat the game if that data is available. Now that you know what we need to build, let’s get started. I’ll introduce relevant concepts as we go. Subscribe to not miss out when future parts of the tutorial are released! Table of Contents How is React Initialized in Our Project? What are React Components? React VS React-DOM Making Our First React Component Creating The Search Component What Are Props in React? What Are Hooks in React? The useState Hook Setting Our Backend to Enable Search Requests to The RAWG API Setting Up Express Defining The /api/games Route Fetching Search Data on The Frontend What is The useEffect Hook and How it Works? How is Data Fetched With useEffect? Creating a Custom Hook react-pdf in action Making Our First React Component Now that you understand how components work, let’s define our first component, the component. If you take a look at again, you’ll notice that we’re already importing this component from a file called . Let’s start from scratch by deleting everything in the existing file and adding the following code which will display the app’s name and logo. The tag will contain all of the content of the search page. We define it as a container with a direction set to . We then center the content within that tag along the cross axis of the container using . Below is a schematic displaying how the axis are defined according to the direction of the flex container. Remember that using will center the content of the flex container along the cross axis while will center its content along the main axis. We then make the tag take the full available width of the page using and give it a padding of 2 with . All of the styling explained above is succinctly described in Tailwind by passing the needed class names to the className property of the JSX element. Now, you might be wondering why is it instead of that you’re accustomed to in HTML? The reason is that there is a difference in how the attribute is named in HTML vs the DOM which the underlying model representing a webpage. Since React operates on the DOM it was chosen to use instead which is what the DOM uses. Now within the tag, we define a which will hold the app’s name and logo. This is set as a flex container with the direction set to by default, so we don’t need to specify it. We center its content along the cross axis so that the app’s title and logo are near each other at the center. We then use an tag to display the logo. You might have noticed that the path to the image does not contain the folder despite the image being in that folder. That’s because Vite will automatically make whatever is in the folder as if it was available from the root of the project, so you do not need to specify the folder in the path. Finally and sets the size of the logo using units defined by Tailwind which are responsive. If you now start a development server using the command in your terminal, you should get the following result. Let’s makes things prettier before moving on. In add the following Tailwind classes to the body tag. We’re setting the background color to be of gray-950 which is a color predefined by Tailwind that is near black. We then set the background image and set it to repeat so that it tiles indefinitely making the image cover the page regardless of the size of the viewport. You should have the following result. As you can see the app’s name near the logo is not visible. To fix the issue, let’s add a nice looking gradient to it. To showcase how you can create custom classes composed of Tailwind classes, let’s add the following in our file. The key here is to use the directive and then use the Tailwind classes you need. Creates the gradient. Makes sure the gradient is applied within the text’s letters. If you go back to our React component and apply the custom Tailwind class we created along with a few other Tailwind classes, you should get the following result. Creating The Search Component In the previous section we created our main React component acting as the entrypoint of our UI. However, we need to define more than just one component in our app. To keep things organized, let’s create a folder under the folder. Within, let’s define our next component, the component by creating a file. Add the following code in . The component is essentially a search bar. You’ll probably notice that we have params that are passed to this component. These are called props. It’s another one of React’s major concepts. What Are Props in React? What makes components flexible in React is the ability to pass props to them to change how they’re rendered. This makes components usable in more than just one screen or one place in the app. You can view props the same way as params that you pass to a function. Here is a basic example of how props are used in React. To pass props to a component you can simply start adding arbitrary attributes to where you call this component. In the example above, I decided to use an attribute called “name” that is then available under the props object where the component is defined. That’s why I can do and get access to “Hello World”. You might have noticed that we use curly braces when putting prop.name within the tag. This is important, so that the actual value, “Hello World” is what’s rendered rather than having literally “prop.name” displayed. A more convenient way of dealing with props is to use destructuring which is a JavaScript concept allowing us to directly get the properties we need. So now, if I need more than one prop, I can do the following. Now that you understand what props are and how they work, let’s further analyze the code for the component. Most of the Tailwind classes here are easy to understand if you already know CSS so I will not go over each of them. I would like however, to explain a few that may not be as straightforward to figure out : : Applies 4 units to the bottom margin of the element. If you had instead, it would be applied for margin left, for margin right, etc… : Applies 2 units to both the top and bottom margin of the element. Therefore, along the y axis. If you where to use , the left and right margins would be increased instead since they’re along the x axis. The attribute allowing us to set the value inside the field. The attribute which takes a function and will run it every time the user types in the field. Which also allows us the get what was typed. Retain the data between renders. Trigger React to render the component with new data (re-rendering). A state variable to retain the data between renders. A state setter function to update the variable and trigger React to render the component again.