Sort GraphQL Data And Display Filtered Results in React

Our homepage is mostly prepared except for displaying the most premium (i.e. highest price) listings. In this lesson, we'll look to make the query we'll need to retrieve the highest priced listings from our server and display that information on our homepage.

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two

Our homepage is mostly prepared except for displaying the most premium or highest priced listings. In this lesson, we'll look to make the query we'll need to retrieve this listing's information and displayed in our homepage. Now, as a note, we've added an additional image asset, listing loading card cover, in this lesson within the home directory. And we'll use this asset when we begin to build the loading states of our listing cards, with which we'll see shortly. Once again, as always, we'll provide the asset or a link to the asset in the lesson manuscript, and we'll provide the asset in our code samples we'll share with you. Now, we've created a root-level listings field that allows us to query for listings from the listings collection in our database, and we're able to apply a filter argument to this field to determine how we would want the results returned be sorted based on price. So with that said, let's create the listings query document in the libgraphqlqu eries folder. We'll create a listings folder and within has an index file. And in the graphqlqueries index file, we'll re-export the soon-to-be-created graphql document for listings. We'll import the GQL tag from Apollo Boost, export a constant labeled listings, and construct our query function. We'll state that it is to accept a filter argument of type listings filter, limits, and page arguments of type integer. And all of these arguments are to be required and are not optional. We'll query for the listings field, pass those arguments down, and for now, all the information we'll need from this listings query is to query for enough information as to be able to populate the listing card component we've set up before, which is essentially a card preview of the listing in question. So with that said, we'll need the result returned to us, which is the list of listings, and for each listing item, we'll get the ID, title, image, address, price, and number of guests fields. With our GraphQL query document established, we'll auto-generate the TypeScript definitions with regards to this GraphQL document, so we'll head to the terminal, and in our client project, we'll first run npm run code gen schema to regenerate the schema in our client application. And when complete, we'll run npm run code gen generate to generate the Type Script definitions. With our GraphQL query document for the listings field prepared, we can now look to query for this field in our homepage component. So the first thing we'll do in the homepage component file is we'll import the use query hook from React Apollo, and we'll import the listings query document and the auto-generated TypeScript definitions for the data and variables for this listings query. Remember how we mentioned that the Apollo CLI creates a global types file when we generate our TypeScript definitions that contains the enums and input object types of our schema in the TypeScript format? When we are to run the listings query in the homepage, we're going to pass in a filter value of pricing high to low to ensure we're going to get the highest priced listings. Instead of passing the value directly though, we can use the enum here to determine we're referencing a value from the correct list of values. So with that said, in the home component file, we'll import the listings filter enum from the global type file. We'll now prepare our query. We'll run the use query hook at the top of the component function, pass the listings data and listings variables interfaces, specify the listings GraphQL document and we'll say we're interested in retrieving the loading and data information from our query results. I believe we might have forgotten to import the actual query document, so we'll go ahead and import the listings query document from the lib GraphQL queries folder. Great. TypeScript now warns us because we're passing in a variables object, but it doesn't match the shape of variables this query is to accept. So let's pass in the variables we intend to pass in. We're expected to pass in a filter variable and we'll use the listings filter enum to pass a value here of price high to low. Limit helps dictate the amount of listings we want returned and since we only want to show four in the homepage, we'll pass a value of four. And since we want the first four listings in our collection or data to say the first page of results, we'll pass a page value of one. And though not really necessary, we'll have the number values as constants outside of our components. With the use query hook now prepared, if we placed a console log message for data, we'll see the four listing objects be returned to us when the query is complete. Great. What we've done before when a query was in the loading state is we'll show a skeleton loading UI for the entire page to convey to the user that data is being retrieved. This was somewhat necessary since we didn't have much else to show the user. With the whole page, things are a little different. In the whole page, the majority of information is static information that is independent of the status of the query. Whether the query is loading or not, the hero, the section below the hero, the images shown to the user can all be shown and we don't have to wait for the status of the listing query to show this information. This is why perhaps what we can do here is have a more focused skeleton experience just around the listing section. We can have three states. One, where the listings query is in flight, we'll show the home listings skeleton components. One, where the listings query is complete, we'll show the home listings components. And if the listing query ever fails, we can just perhaps not show anything. Now we could try and show an error message or something, but from a UI perspective, the way I look at this page is that the home page doesn't depend on the listings. If this particular query was to error, we just don't have to show anything and the user can still move around and see everything else in the home page. Okay, with this game plan in mind, what we can do is have a function called render listings section that will be responsible in consolidating what we've just talked about. So for now, we'll say if the query is loading, it'll display a loading message. That's what the function will do. And if data is available, we'll want to return a child component that we can call home listings that will accept a title prop with a value of premium listings. And it'll also accept a listings prop that is given a value of the listings array itself, with which we can get from the data objects from listings dot result. And if data doesn't exist and loading isn't true, this probably means the query has failed. So we'll have this function return null. And we can run this function in the home return statement to convey what would be shown depending on the state of the listings query. And we can have this function be run right after the call to action section. Something that we're missing is building the actual home listings components. So we'll go ahead and build it now. In the components folder within the home directory will create a home listings component folder with an index file. And in the components index file will re-export the soon to be created home listings child components. The home listings component is going to be very similar to how we showed a series of listings in our user page. However, it'll be even simpler because we're not going to show any form of pag ination. We'll leverage and designs list components and just list the listing items available. So we'll import the react library. We'll import the list as well as the typography components from and design. We'll import the listing card components from the lib components folder. The listing card component is what we've created before and it's going to be the rendered card for each iterated list item. And finally we'll import the interface for the data returned from the listings query from our generated typescript definitions. We'll be using this to help describe the shape of the listings prop that's going to be passed into this component. With that said, let's now describe the shape of props for this component. We'll say this component is to accept a title prop of type string and a listings prop, which is to be the listings array. And this is to have the shape of the result field from the listings field from our auto-generated data interface. Now recall that this is a lookup type for where we're able to access the type of a property within the interface. We'll destruct the title sub-component from typography and we'll create the home listings component function. The home listings component return statement is going to be fairly straightforward. It's simply going to render a title from which it's going to get its value as a prop. And it will render the list component that we've seen before in earlier lessons . We'll apply a grid layout to the list component with the intention of having four items be shown in the list side by side in large viewports and two and one item taking the entire width in small and extra small viewports respectfully. The source of data for this list is the listings prop that's going to be passed in. And for every item we want to render, we'll render the listing card component and pass the iterated listing object down. And in the parent home component, we'll just need to now import the home listings component from the adjacent components folder. And since we're already rendering the home listings component, if we take a look at our home page right now, we'll see the four highest priced listings shown to us. How cool is that? And though we have our home listings component be shown to us, our loading state right now for dissection isn't very presentable. We just have a text that says loading, with which we could probably better catch and see if we made our network connection in the browser console mimic the slow 3G state. With that said, let's prepare a home listings skeleton component responsible in rendering the skeleton UI of our listings section. We'll create the component index in the home components folder. And in the components index file, we'll re-export this soon to be created home listings skeleton components. The home listings skeleton component will be very similar to the home listings component except we're not going to render listing cards, but instead render a custom card we'll create that appears as the card is in the loading state. So with that said, let's copy what we have from the home listings component and make changes as we go. First, we'll remove the import of the listings card component and the generated interface for the data from the listings query. We won't need the typography components from and design, but we'll keep the list component. We'll import two other components from and design that will need to help convey a loading status, the card and skeleton component. We'll also import the listing loading card cover image that we'll use as a cover for the card we want to show here. We'll remove the props interface since we don't expect this component to receive any props. We'll also remove the destructuring for the title and any prop expectations that are going to be passed in. And we'll rename the components function to be home listing skeleton. In the return statement, we'll look to have the loading state we want to convey . So first, we'll update the dev class. We don't want to show a title here and instead aim to show the skeleton component from and design, but have zero rows within that skeleton, and this will be just a single skeleton bar where the title will be instead. For the list, we'll want to return instead of listing card our own custom card. With the cover of the image being the loading gray image we've imported, and the last thing we'll have to do, or think about, is we don't have any data here, but we want to show four items. Or in other words, we want to show four of these custom cards rendered here. The list component simply runs the render item function for every object it can find from data source. So to help mimic the fact that we want to show four items, we'll just create an array called empty data, which will have four empty objects and we'll use that as the value of the data source. Oh, and at design's card component does take a loading prop with which it uses to show a loading indicator in the card body itself. So we'll apply this loading property in the cards we want rendered. And there we have it. In the home parent component, we'll import the newly created home listings skeleton component from the adjacent components folder. And in our render listings section function, when our query is in the loading state, we'll simply return that new child components we've just created. If we head to our application, and if we actually want to see this loading state in a more apparent setting, we'll once again change our network conditions and throttle our connection to slow 3G. We'll refresh our page. And there we go. We see the custom loading indicator or loading section that we 've set up for this listings. Another probably more obvious way to actually see that would be if we just simply change this loading statement to true temporarily. It doesn't seem to like that. So because we have to specify data, that's fine. At this moment of time, we've done what we needed to do. Or actually, yes, let's just do that. I'm going to specify true here. I'm going to comment this out temporarily. And there we have it. This is essentially the loading skeleton or the home listings skeleton we've set up and will be shown when the listings query is in flight. Now, keep in mind, you might be thinking that this is overkill for a loading state and you might possibly be right. Like how you want to convey the loading status of this query or other queries is completely up to you. We could have perhaps just shown a spinner from AntDesign. We could have just shown text that says loading. We could have just tried to pass a loading property to the listing card itself to control how loading will behave in that condition. We could have displayed nothing. There's many different options and it's really completely up to you. We've just wanted to create this loading state to mimic the perceived outcome of what the actual layout would be. And when we actually build the listings page, we're going to do something very similar and we'll build our loading state for that page to sort of mimic how the expected layout would look like. Before we forget, we'll uncomment the portion we've commented in our render listings section function. And instead of saying if true, we'll say if the loading status is true, we'll then show the skeleton. Otherwise, if data is available, we'll show the home listings component. Fantastic. So now if we took a look at our page, we should see the home listings component be shown after data is finally available. [BLANK_AUDIO]