Playing
01. Infinite Scrolling with Inertia

Episodes

0%
Your progress
  • Total: 29m
  • Played: 0m
  • Remaining: 29m
Join or sign in to track your progress

Transcript

00:00
Okay, so let's build out a super smooth, fast, infinite scroll in Inertia.
00:06
I'm going to show you how this works and then we're going to dive in and pretty much build this up from scratch because there are a couple of really important things we need to do along the way. And we're also going to be refactoring this because we're going to see a slight issue
00:20
with the reloads directly with Inertia's router. Okay, let's demo this out. I've got 100 records in the database here ordered by latest first and as you can see as I start to scroll, more get loaded in and this is really, really smooth.
00:34
There's a specific technique that we can use to do this and it's also really fast as well. So there's a particular thing that we can do to make this quicker than standard pagination which we are going to be covering. So that's pretty much it.
00:48
Obviously when we get to the end, nothing happens. We sort of cut out any loading so nothing else loads and that is pretty much our infinite scroll. So, now we know what this looks like and what we're going to do, let's head over to a fresh
01:01
project and build this out. Okay, so I've gone ahead and created a fresh Laravel Breeze project so we have Inertia up and running. I've also gone ahead and created a route, a controller and a view in here where we can
01:14
list through all of our posts. We're going to create the model and the seeder for the data out from scratch just so I can show you a technique that I like to use to see data in a specific order like it had been naturally posted.
01:28
So let's get started with this. Of course, you can skip ahead if you've already got data that you want to go ahead and paginate but the first thing that we're going to do is go ahead and make out this post model. Of course, we're going to create a migration with this and we're also going to create a
01:41
factory out for this as well. So let's head straight over to the create post table migration. We're going to keep this really, really simple. We're just going to have a title in here so let's go ahead and give out a string for
01:52
the title and we'll go ahead and create out some text in here which is just going to be a teaser. So we'll just leave it at that for now. I think that's pretty much everything we need.
02:01
So let's go ahead and migrate this out. Okay, so let's head over to our post factory and we'll just fill this in really roughly. So we'll go ahead and fake out a sentence here for the title. Let's do six words for that and we'll do the same for the teaser but we'll bump this up
02:17
to say 30 and that is pretty much it. So we could do this manually but with this, I'm going to go ahead and create out a seeder specifically. So let's make out a seeder here called post seeder and let's go ahead and open that post
02:32
seeder up in here and with this, we would normally just go ahead and use this to grab out the factory instance. We would choose how many times we want this. So let's say we wanted 100 fake posts in here and then we would just go and create them.
02:46
The problem with this is what we're going to have is a very similar created at date for every single post that we have which can be a little bit of a problem because when we're creating an infinite scroll, we want to make sure that everything is nicely lining up.
03:00
So what I like to do when I'm generating out content that has been posted after each other, so we would create one blog post one day, another one the next day, is go ahead and pull in the state method with our factories and with this, we can pass through a new sequence and into here, let's go ahead and just do an inline short closure here.
03:21
We get a sequence passed in for each iteration of this. Then we just return an array with the columns that we want to fill. So in this case, I want to fill in created at and I want to set that to now but I want to increment this for every single post that's created.
03:36
So what I'm going to do is with that carbon object, I'm going to go ahead and add on say a day, a second, it doesn't really matter what you do and then we can use the sequence index for each iteration of this to increment that by one day. So for the first one, this will be zero, so it won't add any days, it will just be today.
03:54
The second one will be another day and so on and so forth, so we'll just keep adding days to this and of course, this is really rough but it will do the job. So now that we've done this, we can go ahead and seed our database. So let's say phps and dbseed and we'll specifically choose that post seeder and that should be
04:10
done. So if we head over to the database here and look on the posts, you can see that the created at dates increment for each one. So it gives us a really nice order to work with when we're implementing something like
04:21
this. Okay, so the next thing that we're going to do is head over to our post index controller here that's already been created and we're going to just dump a list of all of the posts in the right order, iterate through them and then we can start looking at these techniques
04:36
to implement our infinite scroll. So to do this, let's go and first of all just grab out all of the posts that we need. So of course, we're just going to say post, we'll use latest here and let's start out by paginating this.
04:50
We are going to change this up later but let's paginate these by 10 just so we have some sort of pagination involved because obviously we don't want to output all of the posts in one go and we'll go ahead and return these from here. Now to make this really quick and just in general, when we're passing data through to
05:06
Inertia, we should always use API resources. So let's go ahead and quickly create out an API resource. So we'll use make resource, create out a post resource and with that done, we can pretty much just say post resource and collection and we can pass our post through to here.
05:23
That will also give us our nice metadata that we're going to need to load more posts and push them onto the stack. So our post resource at the moment just returns all of the data within that model but let's go ahead and shorten this just to get the data that we need.
05:37
So we use our ID, we'll go ahead and switch this out for the title and we'll do the same thing for the teaser as well. So now over in the view that we've created over in posts, we can go ahead and accept this in with define props and let's go ahead and say that we are accepting in a post which
05:55
is just going to be an object and then just dump these out to the page just to make sure we've got all of this data. Okay, that's looking good. You can see that we've got a data wrapper here with the actual data in and then down
06:06
here we've got all of our metadata which contains our pagination information which is exactly what we need. So I'm just going to go ahead and create out a really simple container in here and just iterate through these.
06:16
I'm going to style this up purely for the reason that we want a nice height on all of this to be able to scroll. So let's create out a container here with a max width of 2xl, set the margin on the x-axis to auto and we'll set a margin on the y-axis to 12 and we'll space out all of the
06:32
items in here by 12 as well. So each of these divs inside of here is going to be each of our posts. So let's go ahead and start to iterate through these with a v4 and we'll say post in posts and let's go ahead and key this, post ID and then in here let's go ahead and output an
06:51
h1 for each post title. We'll also include the post ID as well just so we can keep track of where everything is. So let's say post title and down here we'll have a paragraph and that will just be the post teaser.
07:04
So we've got plenty of information that we can start to play around with. Okay so nothing is coming through at the moment just because we need to access that data key and there we go. So let's add a tiny bit of styling here just to make things look good, why not, and we'll
07:16
say font bold for the header and text 3xl and then for the paragraph let's just go ahead and say margin top of 2 and text large and I think that is enough. Okay so we've got 10 records on the page, obviously our goal is when we get to the bottom we want to start to load more in.
07:34
To do this we use the javascript intersection observer API, sounds a lot more complicated than it is but essentially this will just tell us when we see something on a page. Now we can implement this ourselves or we could use something like use intersection observer composable from the Vue.use collection of things.
07:55
So I prefer to do this, it's just a lot easier, the API looks a lot better. So we're going to go ahead and roll with this and I'll show you exactly how this works. So we're going to head over to the get started guide and just go ahead and install Vue.use so let's go and just pull this in straight into our project and then we'll head back
08:12
over to the use intersection observer composable and see how we get this to work. So we're going to go ahead and import this here at the top so let's go over to just the top of our page here and let's get started. So I'm going to do this just underneath where we've defined out our props.
08:27
Now the first thing that we need is an element that we want to see to be able to load more posts. Now this is always going to be at the very bottom of the current list of posts that we have.
08:38
So if our component here is what we want to see to load more, this is always going to be at the bottom. So we've currently got 10 posts in this stack, when we push another 10 to it, this will push this thing down and then we will scroll down even further, see this again, load more in
08:57
and that's pretty much how infinite scrolling works. We have something at the bottom of the page that we observe to know when to load more in. So what this does is we're going to end up just constantly making API requests to load
09:08
more posts in. So we'll go ahead and add a ref to this element and we'll just call this last, you can call it whatever you want and this is what we're going to look out for. So this is where we want to implement watching for this with our intersection observer API.
09:20
So to use this package we just literally go use intersection observer. Into this we pass the thing that we're looking for. So in view to grab a component, an HTML element, we just go ahead and create our ref so we just need to go ahead and import ref in here from view and that will now contain that specific
09:44
element so we can pass that in and watch for that. Into here we get in, this is incredibly important, whether or not this is intersecting. So we're going to say is intersecting and then for the callback here let's go ahead and define this out and I'm just going to console log out whether this is intersecting
10:04
and we'll see what happens. So we're watching this element for when it intersects, let's see what happens. Okay so I'm going to go ahead and open up my console, at the moment we get false as soon as we hit the page.
10:16
As we scroll down we don't see anything console logged out until we get to the very bottom and then we see true. If we go up again we get false, back down we get true but obviously we're going to be loading more posts in so the user will be able to continually scroll, that element is
10:29
always going to be at the bottom so that is when we're going to start to load more posts in. So this is at the point where we can say something like well if we're not intersecting then we can just return because we don't want to do anything if we are not seeing that element
10:42
or we're going to end up making too many requests here. So now we're going to look at a way that we can actually load more posts in using Inertia's router which we are going to switch out, we're going to see why this is probably not the best solution.
10:56
So we're going to go ahead and import the router from Inertia which allows us to make requests to our page and we're going to go ahead and say router and we can just say replace or reload would probably be a little bit better and in here we just reload this with any data that we need.
11:12
So if we think about this we've already got pagination set up so if for example I were to manually in the query string say page 2 that's actually loading in the second page for us. So if we make another request through to this page we just increment that page we should
11:28
get the new data loaded in. So let's try this out first of all and then we will obviously see what problems we come up against. So the data that we want to send through is the new page.
11:38
Let's just hard code that to 2 for now just to see what happens. When I scroll all the way down when we get to the bottom what you'll have noticed is we do get page 2 in the query string but this has just replaced all of the data which is not what we want.
11:51
We want this to add on to what we've already got. So let's go ahead and switch out the page for the actual page. This comes from our props remember we saw that dumped out earlier so let's go ahead and assign a variable to our props and then here this is going to be props and posts and
12:08
then it will be meta and then it will be I think let's have a look next page. Well we could just increment this so we could use the current page and then increment that. Let's just try that out and see what happens. So we'll start on page 1 and we'll come down and then we'll go down again and down again
12:25
and sure enough it is loading in the next page which is exactly what we want. But we want to push 2 we want to push this data to a new stack. So this is where things using a NERSH's router start to get a little bit complicated. We will do it this way but then we'll switch this out for a much better solution.
12:42
So I kind of want to keep our post state completely separate so I'm going to initially grab in the data that we have for our post and I'm going to iterate over this instead. So let's come down here and switch this out let's go over and you can see it works in exactly the same way but now that we've got a local variable that's holding this we can
13:02
push to this all of the new props that are coming through. So let's pull our data up because we don't really need to pull that down and let's go ahead and implement an on success callback in here which will go ahead and pull in that new data.
13:15
So we could just say postState.value because we're working with a ref now and we can set this to a new array and the first part of this array is going to be the old postState so we can go ahead and spread out postState.value and then we can go ahead and spread out the new props that have come in so props.posts.data and then this should go ahead and keep the
13:39
old post but just push onto an array the new set of values. So let's just try this out come down to the bottom of the page and yeah sure enough that's loading in the next set and loading in the next set and so on and so forth all the way down to the bottom.
13:55
So technically we've implemented infinite scrolling now. Now there's a couple of issues with the solution just here. The first one is that when we scroll down onto say page three or four and we refresh the page because inertia automatically pushes this to the query string we are only on page
14:15
five unless we're implementing bi-directional infinite scrolling which is incredibly difficult to do we can't go up and see the previous post to this. Ideally with infinite scrolling we want the user to be able to scroll down the page but when they re-land on the page we're just going to start from the top again so this is a little
14:31
bit annoying having this inside of the query string. Now there are ways that you can get around this by overwriting the query string manually but I don't really like doing this. We ideally want to just not push to the query string at all.
14:46
Now unfortunately with inertia we cannot do that so we're actually going to go ahead and switch this out to just make a plain request using Axios. So let's go ahead and see how this looks. So the first thing that I'm going to do is go ahead and import Axios at the top.
15:01
This is already included with Laravel but if you're working with something else or you don't have it you're going to want to make sure you install it first of all and we'll go ahead and get rid of router reload in fact we'll keep this just as a reference so we don't have to type too much out again.
15:17
So with Axios we can go ahead and just make a standard get request to the URL that we want to see. So in this case this is going to be the current page plus the new page in here. So I'm just going to go ahead and dump out once again all of our posts just so we can
15:33
see what this metadata looks like. So let's go ahead and look for meta in here and we've got current page. Now we have a bunch of links in here so we could actually change this to simple pagination but let's just manually build up this in here in fact we don't need to we've got next in
15:48
here so let's have a look we've got meta and oh we've got links in here so let's use links for now. We are going to switch this up to use something different in a minute. So we want post.linksandnext let's have a look at what that's called I think it's just
16:03
called next. So let's just check that and yeah so we'll just use this for now. So now we are switching over to Axios we can go ahead and make a get request to props posts and links and next perfect.
16:19
So with this once we've made a get request to this we can then go ahead and use a promise to just console log out the response that we get back from here so let's pull this response in and just see what this response is because this is where things start to get a little bit tricky.
16:34
So we're going to go ahead and scroll the way down to the bottom and here is our response so data is just the way that Axios wraps the data that we get back from the request and as you can see it's just HTML that's just the way that inertia works when we go ahead and render this from our post index controller this is just going to render out an HTML page
16:55
or HTML content that we can use to hydrate everything on the page it's not really what we want ideally so we need to figure out a way to just get back the posts that we need inside of here. There's a couple of ways that we could do that we could create out an entirely new controller
17:12
that specifically returns just that JSON data that we need so the next lot of posts or what we could do is head over to our post index controller and detect inside of here if we are requesting JSON and then we can return just a plain collection of posts using our post resource so to do this we can go ahead and create out an if statement we can pull
17:36
in our request in here and we can use a handy method on this called needs or wants JSON I think it's called wants JSON so if we want JSON we can go ahead and return JSON let's just see what that looks like let's scroll down to the bottom and yeah sure enough this data now just contains an empty array so that's really good because then we're working within
17:58
the same controller we've kind of got a dual purpose controller that will return to us what we need depending on what we're doing so what do we want to return here well we just want to return a collection of posts using our post resource and we can just do this because the query string will already contain that page so return page two three
18:18
whatever we need so let's head over give this page a refresh scroll down and let's just take a look at the data we get back perfect so we've got our data we've got our links so we can update our meta we've got everything we could need inside of here so let's go ahead and switch this up now to do pretty much what we did before and now obviously using our
18:38
new Axios request so we can push to our post state value so we can push to the previous list of posts that we already had and now we get our response data and let's just double check this data again and we should be good let's just try this out so let's go to the top let's scroll all the way down to the bottom and yeah sure enough it is still working and
19:04
it's loading more in but of course because we're making a plain old request here it is not pushing to the query string now you'll probably notice if we scroll down here what is actually happening is we're kind of making us the same request to page two every single time we can see that in our network tab which is obviously not what we need now the reason
19:22
that this is happening is because we're now making an Axios request the props data is not getting updated so it's not getting the next metadata back that we actually need now rather than try and fiddle around with this right now I'm going to go ahead and show you a much better way to implement pagination when you are dealing with something like infinite
19:41
scrolling and infinite loading so we're going to completely change this up and we're going to head over to our post index controller and instead we're going to use cursor pagination I'm not going to go ahead and explain how cursor pagination works in detail because it's all listed on the lower of our documentation it's a little bit complex but if we just switch
20:00
this over to cursor paginate and head over to our post page you can still see we still get 10 results in here so it's still working as we would expect let's just go ahead and dump out though what we get back from this just so we can see the difference in how this looks so let's go ahead and just dump out our posts in here of course we get all of
20:20
our data as normal but down here you'll see that for the meta inside of here we have a path which is going to be really handy we still get stuff like how many are being displayed per page but this time we get a next cursor and a previous cursor so this is like a hash that gives us back the sort of position that we need to be in now this is a lot faster
20:43
because it doesn't rely on specific database queries to fetch out how many you need again all of that stuff is on the lower of our documentation so you can go ahead and check it out but generally this is a lot faster to actually make requests we might even be able to see that so let's just go ahead and to just demonstrate this out really quickly so let's switch this back
21:01
to pagination might not be too much faster because we don't have a lot of data in here but let's go ahead and just scroll down and just look at the request time that we have for this for example we've got 50 milliseconds let's switch this back over to cursor paginate and let's go down and you can see that it's at 33 so a massive improvement plus if we
21:23
had a lot more data in here the time difference would be bigger as well so we want to make this as quick as possible we don't really want the user to be waiting around for more data to roll in okay so now that we have switched this over to cursor paginate we need to change up how we are making a request via Axios to get this based on the next cursor so let's
21:43
go back to how we were at the top here and we can get rid of this dump just out here for now and now we're going to make a request here and we're going to have to build this URL up so we know that this comes from our props posts meta and this is our I think it was now let's bring this back actually to see what we've got so I always forget what
22:05
these are called so let's go down to our meta here and yes path so we want to build this up and say path and then instead of saying page equals we want to say cursor equals that's how this new method will pick up the position that we're in so the cursor is just going to be the next cursor so this is just next underscore cursor so we can say props and
22:27
posts and meta and next cursor okay so let's leave this how it is and let's just see what this looks like so we'll go ahead and keep up our network tab and we'll go ahead and get rid of the dump out here and let's scroll and see what happens so I'm going to go all the way down to the bottom and yeah we get an error let's just have a look here cannot
22:46
read meta let's go up here and have a look hosts there we go try that again and there we go sure enough it's loading in the next page now if we keep scrolling you can see that we get the same problem we're still loading in the same cursor position every single time that is just because the way that we've set this up the meta is not being replaced every
23:12
single time we do this because we're making a manual request to get this data back but this data is not being refreshed which is a problem so we're going to switch this up and actually get rid of our post state because technically we can mutate our props in this case this is absolutely fine I'd always be wary of mutating the props directly but since
23:31
this is an infinite scroll and we want our props initially but then we want to slightly modify them I think this is fine so I'm going to get rid of post state and we're going to go ahead and just iterate through post.data as we normally would but then we can change this up to specifically hit our props so we're going to say props.post.data and we're going
23:50
to replace that array out with props.post.data spread out so the current data that's currently being passed through our props and then just pushing on the data that we've got here but what we can now do is we can update the meta as well so the next cursor will always be read every single time here so at the moment next cursor is always the same because the
24:11
meta is never being updated but now what we can do is say props.post.meta and we can override this by getting the meta back from our Axios request so now the next cursor is always going to be different on every single time this observer hits and then we should just see the next page roll in so let's scroll down we get 90 and down a bit more we get 80 perfect
24:37
so now we can scroll all the way down and this is working really nicely there's still a bit of a sort of lag when we get to the bottom of the page which is something that we're going to solve but that is now a lot faster and we've got it working which is of course really important so we've got two more things to do the first is if I just scroll
24:56
down really quickly here we get this sort of slight lag in between it's really difficult to see but if we actually pull up our console you can see there's a slight sort of jump in between loading the next set of results we want to make this super smooth now the solution to this is incredibly simple if you think about the way this works at the moment
25:14
if we just go down to where our last element is here and we go ahead and maybe just set a width to this of full a height to 12 and we'll set the background to black just so we can see this if we scroll down we probably actually will just about miss it but you can see it flicker at the bottom of every single time we go right down to the bottom of the
25:36
page now it would be a lot better if as the user was just approaching the end of the list we then started the process of loading in more posts what that would mean is before the user hits the last post we would already start to load more posts in now the trick here is literally just applying a CSS transform to this to push it slightly up the page and
26:04
to do this in Tailwind at least we're going to go ahead and set a minus translate on the y-axis to pretty much any value you can experiment with this so for example I'm just going to choose 32 what that now means is at the bottom of the page this is just slightly pushed up so what that means is by the time we just about hit the bottom it's already going to start loading
26:27
more posts in you saw we didn't even get to the bottom of the page and it started to load more in so as I scroll down you can see now that this is pretty smooth we're not seeing any sort of jump when we get to the bottom of the list so of course you can experiment with that but that's just a trick that I use with infinite scrolling just so we can always sort of preload in the post
26:48
before the user even gets to the bottom now at the moment everything looks pretty good but let's scroll all the way down to the very bottom of all of the post and just see what happens so I'm going to go all the way down we're nearing the end of this now and yeah we've kind of started again now you might want that functionality if you do feel free to just leave now but I don't really want
27:09
I want the user to know that they are at the bottom of the page otherwise it's a little bit confusing we've got ids here so we can see that we've started the list again but doing this and just sort of in this real infinite scroll of content doesn't really make sense so what we want to do is stop the intersection observer once we reach the end of the list how do we do this
27:31
well with the package that we've pulled in and this composable what we actually get out of this returned is the ability to stop this so let's go ahead and take this stop function out of use intersection observer that's returned and then we can just stop the intersection observer functionality working under some sort of condition now the condition is that there is no next cursor so if
27:54
we don't have a next cursor that just means that we're at the end of the list so really simply in here while we are running this Axios request and inside of here we can check if the response does not have a next cursor and then we can just stop the intersection observer running so let's say response data meta and next cursor and we're checking if that is null you can compare it to
28:18
null if you want to we're just going to go ahead and invoke stop and that's pretty much all we need to do this will now not work anymore and it will stop the intersection observer api functionality so now i can scroll all the way to the bottom and sure enough when we hit the bottom that intersection observer is now no longer firing and it means we know that we've reached the end of the
28:40
page so there we go we have built out a infinite scroll using inertia making a manual Axios request using cursor pagination so it is really quick implementing that css offset so we have nice smooth scrolling all the way down without any jumping and of course dealt with things like getting to the very bottom of the page and stopping when we get to our last piece of content
1 episode 29 mins

Overview

Want super smooth, fast infinite scrolling in Inertia? In this snippet, we'll seed our database with a bunch of data and start building the solution, refactoring along the way until we perfect it.

Alex Garrett-Smith
Alex Garrett-Smith
Hey, I'm the founder of Codecourse!

Episode discussion

No comments, yet. Be the first!