This video is available to students only

The UserListings & UserBookings React Components

In this lesson, we'll continue to build the user page in our client application by looking to query and present a paginated list of listings and bookings for a certain user.

📝 Documentation on the <List /> component we use from Ant Design can be found - here.

In the last lesson, we've been able to query for a single user and display some of that queried information in the /user/:id page of that user. In this lesson, we'll look to query the paginated list of listings and bookings for the user and display it on the user's page.

  • The listings section will be a child <UserListings /> component of the user page.

  • The bookings section will be a child <UserBookings /> component of the user page.

<UserListings /> and <UserBookings /> resemble one another based on the cards being shown. The UI for these cards is going to be used in many different areas of our app including the /home page and the /listings/:location? page.

Since these listing card elements are going to be used in multiple areas of our application, we'll create a <ListingCard /> component to represent a single listing element in our application's src/lib/components/ folder that can be used anywhere. The <ListingCard /> component will be fairly straightforward - it will accept a series of props such as the price of a listing, the title, description, numOfGuests, and it will display that information within a card.

Update User Query#

The first thing we're going to do is update the user query document in our app to query for the bookings and listings fields of a user. bookings and listings are paginated fields that require us to pass in a limit and page arguments. We're going to be passing these arguments from the <User /> component that makes the query so let's state the User query document we've established in the src/lib/graphql/queries/User/index.ts file is going to accept a some new arguments.

We'll state that the User query is going to accept a bookingsPage argument that will determine which booking page the user is in and will be an integer type. The User query will also accept a listingsPage argument that will determine which listings page the user is in. Since we'll have the same limit for the number of bookings or listings that can be shown on a single page, we'll only specify a single limit argument is to be passed into the User query.

bookings#

In our user query statement, we'll now query for the bookings field and pass the limit argument along for the limit of bookings we want in a single page. We'll also say the value for the page argument for the bookings field will be bookingsPage.

We'll now query for the fields we'll want from within the bookings field.

  • We'll query for the total field to get the total number of bookings that are returned.

  • We'll query for the result field which is the actual list of booking objects. In each booking object, we'll query for:

    • The id of the booking.

    • The listing of the booking which we'll further query for the id, title, image, address, price, and numOfGuests of the listing.

    • The checkIn and checkOut dates of the booking.

listings#

The listings field we'll query from the user object is going to be very similar to what we query for the bookings field except that there's no checkIn and checkOut information. The result field of listings is the list of listing objects we'll query where we'll get the id, title, image, address, price, and numOfGuests of the listing. We'll also ensure we provide the limit and listingsPage values for the limit and page arguments the listings field expects.

With all these changes, the USER query document we've established will look like:

There shouldn't be a reason for us to update the schema in our client application since the schema has remained the same from the last lesson but we'll now update the autogenerated type definitions for our GraphQL API in our client.

We'll head to the terminal and run the codegen:generate command in our client project.

<ListingCard />#

With our autogenerated type definitions updated, the query we've established in the <User /> component will currently throw an error since we're not passing in the additional variables the query now accepts (bookingsPage, listingsPage, and limit). We'll come back to this in a second. First, we'll create the custom <ListingCard /> component that our upcoming <UserListings /> and <UserBookings /> components are going to use.

We'll create the <ListingCard /> component in the src/lib/components/ folder.

In the src/lib/components/index.ts file, we'll re-export the <ListingCard /> component we'll shortly create.

The <ListingCard /> component we'll create will mostly be presentational and display some information about a single listing. In the src/lib/components/ListingCard/index.tsx file, we'll first import the components we'll need to use from Ant Design - the Card, Icon, and Typography components.

We'll expect the <ListingCard /> component to accept a single listing object prop which will have an id, title, image, and address fields all of which are of type string. The listing object prop will also have a price and numOfGuests fields which are to be number values.

We'll destruct the <Text /> and <Title /> components from the <Typography /> component. We'll create and export the <ListingCard /> component function and in the component, we'll destruct the field values we'll want to access from the listing object prop.

In the <ListingCard /> component's return statement, we'll return the <Card /> component from Ant Design. In the <Card /> component cover prop, we'll state the backgroundImage of the cover is to be the listing image. The rest of the contents within the <Card /> component will display information of the listing such as its price, title, address, and numOfGuests.

This will pretty much be the entire <ListingCard /> component. We'll make some minor changes to it when we survey and see how it looks in our client application.

Icon is a useful component from Ant Design that provides a large list of icons that can be accessed by simply providing a value for the icon's type prop. In <ListingCard />, we've used the <Icon /> component and provided a type value of user to get the user icon.

<User />#

We'll head over to the <User /> component and first look to update the user query being made. The query for the user field now expects three new variables - bookingsPage, listingsPage, and limit. For the bookings and listings page values, we want our <User /> component to keep track of these values and update them based on which of the pages the user wants to visit. As a result, these values will be best kept as component state so we'll import the useState Hook in our <User /> component file.

At the top of our <Listings /> component, we'll use the useState Hook to create two new state values - bookingsPage and listingsPage. We'll initialize these page values with the value of 1 since when the user first visits the /user/:id page, we'll want them to see the first page for both the bookings and listings lists.

We'll also destruct functions that will be used to update these state values.

Since the limit value (i.e. the limit of the number of bookings or listings that should show for a single page) will stay the same and we won't want the user to update this, we'll create a constant above our component called PAGE_LIMIT that'll reference the limit of paginated items in a page - which will be 4.

In our useQuery Hook declaration, we'll then pass the values for the new variables - bookingsPage, listingsPage, and limit.

We'll now have the <User /> component render the child <UserBookings /> and <UserListings /> components before we create them. In the <User /> component, we'll check for if the user data exists and if so - we'll assign the listings and bookings fields of the user data to the constants userListings and userBookings respectively.

We'll create constant elements for the <UserListings /> and <UserBookings /> components. If the userListings constant has a value (i.e. listings within user exists), we'll have a userListingsElement be the <UserListings /> component. For the <UserListings /> component we want to render, we'll pass in a few props that the component will eventually use such as userListings, listingsPage, limit, and the function necessary to update the listingsPage value - setListingsPage().

We'll create a similar userBookingsElement constant that is to be the <UserBookings /> component when the userBookings property has a value (i.e. bookings within user exists). The <UserBookings /> component will receive the following props - userbookings, bookingsPage, limit, and the setBookingsPage() function.

In the <User /> component's return statement, we'll render the userListingsElement and userBookingsElement within their own <Col />'s. With all the changes made in the <User /> component, the src/sections/User/index.tsx will look like the following:

We'll now look to create the <UserListings /> and <UserBookings /> child components we render within <User />. We'll create the folders for them in the components/ folder within src/sections/User/.

In the src/sections/User/components/index.ts file, we'll re-export the soon to be created <UserListings /> and <UserBookings /> components.

<UserListings />#

We'll begin with the <UserListings /> component. The main component we're going to use from Ant Design to help us create <UserListings /> is the powerful <List /> component.

The properties we're interested in using from Ant Design's <List /> component is:

  • The grid prop that helps control the structure of the grid.

  • The dataSource prop which would be the list of data that is going to be iterated and displayed in the list.

  • The renderItem prop which is a prop function that determines how every item of the list is going to be rendered. We'll be interested in rendering the custom <ListingCard /> component for every item in the list.

  • The pagination prop to help set-up the list's pagination configuration. The pagination prop in the <List /> component is adapted from Ant Design's <Pagination /> component which will allow us to specify the current page, the total number of contents, the default page size, and so on.

In the <UserListings /> component file (src/sections/User/components/UserListings/index.tsx), let's begin by first importing what we'll need. We'll import the <List /> and <Typography /> components from Ant Design. We'll import the <ListingCard /> component from our src/lib/components/ folder. We'll also import the autogenerated User interface for the user data being queried.

We'll then declare the props that the <UserListings /> component is to accept - userListings, listingsPage, limit, and setListingsPage. listingsPage and limit will be numbers while setListingsPage will be a function that accepts a number argument and returns void. For the userListings prop, we'll declare a lookup type to access the interface type of the listings field within the User data interface used to describe the shape of data from the user query.

Start a new discussion. All notification go to the author.