Implement Infinite Scrolling in React using Intersection Observer

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey fellow developers welcome back to a brand new video so today we will see how to implement in finite scrolling in react using the intersection Observer API so without any delay let's get started we will be building this mini project to learn its implementation so this is a very simple web app which shows us a list of products and initially only nine products will be loaded and as we scroll down another set of nine products gets loaded and this will keep on happening until all the products are finished loading so we have achieved this using infinite scrolling and under the hood it is using the intersection Observer API to implement this Behavior so now let's get into the code and see how we can actually implement it okay so since we will be using react I have initialized a react application using Create react app and also to save time I've created some components like the product card the spinner so that we mainly focus on implementing the infinite scrolling feature using the intersection Observer API also I am running the application on localhost 3002 which is this and here as you can see it's an empty application so now before actually writing code let's first learn what is the intersection Observer API so let's first see what mdn has to say about the intersection Observer API but before that you have to know that the intersection Observer API is a browser API which means that it can only be used in browsers you cannot use it in node.js or any other JavaScript runtime similar to the fetch API which we usually use to make API calls so now what does it actually do so if we go by the mdn definition it says that the intersection Observer API provides a way to asynchronously observe changes in the intersection of a Target element with an ancestor element or with a top level documents viewport so this seems a bit complex so let's understand this using our example itself so in our example this visible viewport is our parent element or the root element and if I scroll down you can see that we have a spinner right so this spinner is our Target element so basically using the intersection of a server API we are checking when our Target element is intersecting the parent element or is appearing in the viewport so whenever it appears or intersects our parent element we make an API call to fetch more products so similarly you can use this functionality to implement some beautiful animations and many other things but in our case we have used it to implement infinite scrolling so in a nutshell the intersection Observer is a browser API that allows you to track when an element enters or exits the viewport or any other container and just so you know the parent or the root can be any other valid HTML element and hence depending on the target element and the behavior which you desire to implement you can choose the appropriate parent now finally let's get into coding okay so as I already mentioned I have created few components beforehand like the message box the product card itself the spinner and also the products so if you see on our app.js file I am importing products component so now let's go into the products and this is where we will actually write all our business logic like fetching our API using the intersection Observer API we will do all of this here on the products component so to get our products we will be using this dummy json.com API and if you go into this URL you will see that there is a documentation so if I scroll down our point of Interest is this one limit and Skip products because this is what will enable us to implement pagination on our web app so I will just copy it and paste it here for reference right so now I'm going to create a function called let's say fetch products and this will be an async function Also let's close the sidebar and since this is an async function we will be using try catch with it also I will be using the finally so now when we fetch products I want to show a spinner and also if there is any error by fetching the products I want to display an error message so in order to do that I will be creating two State variables first one will be let's say just loading and its default value will be false of course second one will be let's say is error and its default value will also be false now so when we actually fetch our products I want to set the loading state to Blue so in order to do that I will be doing set this loading true and let's say if by chance or is error variable or state is true I want to make it false or basically I want to reset the is error state to false and then here we will actually call the API which is this one so in order to do that I will be using the native fetch API sorry so let's copy this and I will do a template string because I want to change these values dynamically I will show it after one so now let's store whatever response we get in a variable called RS Pro which basically means response promise and then I will do oh wait press Pro dot this and then I will store the final first value in a variable called s which means response I will also console the value so that we can see what response we're getting and in the catch block I will be doing settings error true basically if there is any error I want to set the is error state to true and then inside the finally I will be setting our loading state to false this is because regardless of whether our API succeeds or fail our code will always come to the final block so this is where we should reset our is loading state or clear our is loading state in false because we don't want to show the loading spinner infinitely so now we have defined our function fetch products so now actually let's call this function and see what response we get so in order to call this function I will be using use effect because basically I want to fetch the products why not component actually mounts so now let's copy this paste it over here center console logging it let's inspect it inverted response we are getting so if I expand this you can see that I have 10 products since I've set the limit to 10 I'm getting old 10 products and if I change it to let's say 20 I will be getting 20 products so one thing to keep in mind is that this dummy json.com API doesn't have proper support for pagination and by this what I mean is that I think you all have used this pattern where we specify the page number right so suppose let's say if I specify page equal to 3 it should mean that I want to fetch the page 3 of our products but they don't support this Behavior by default so we will Implement our own custom page Behavior using limit and Skip itself so we will be implementing this a bit later but now let's see how we can use this data to show our products card so to show our products card I will have to store this products in a state variable right so let's create one another state variable called Data and its default value will be an empty array and here I will basically check if products is an array like this and then I will also check if it's an array then it should have at least some products so in art is to be specific at least one product and then we do set data as dot products now let's use this data state render our products and also to fasten up our development process I have already created the CSS files beforehand so for this products component we have a CSS file which is this so this will be classes dot or you can say style starter but in order to actually use this CSS which we've written I will have to first import it like this right so I've already imported it and then I'm using Styles dot root over here so now we have an error state right so what I will do here is if we have an error I will render sh box component which is this one and this accepts a prop call message so here I will write my own custom message so in this case I will write fail to fetch products and then if you don't have any error we then finally show our is loading state so when it's loading I want to show a spinner right so the spinner component is this one so let's import it and if you see we can give a text a default text so I will put a default text as loading products and then if loading is actually finished we will then render our products so let's put a div over here let's close it and and then inside of this diff I will do data dot map and product and then as I already mentioned I have created a product card right so let's use this product card to show our product so I'm going to import it again product card and if you go to a product card component you can see that it accepts image title and price so we will pass all the three props before that I will specify key and in our case it will be product.id so if you go here and let's expand one single product you can see that we have an ID we also have price titled and so in our case we need a title so the title will be product dot title and then we also have price so let's do that product dot price and then finally we have images so you can see that we have four images for a single product but we need only one so to do that I will be doing product dot images and since we know that images is an array I will just pick the first element of the array and then I also have created one CSS organs last name with some CSS for the product cards container which is this one cards container so let's use it over here and with this now if I save I should be able to see our products and yes we can see our products so since we have set a limit of 20 we are getting 20 products but I just want to shoot 10 products and yes now we are getting 10 products let's remove this okay so now we have our basic layout setup so now let's integrate in finance crawling using intersection Observer okay so now let's add the spinner component where we will actually attach our Target that you get from the intersection Observer so buy the spinner component what I mean is this spinner that will just saw right so since we want this to appear at the bottom I will be adding it over here so let's use the same spinner component that we created right and this time the text which I will pass will be loading more products now for the intersection Observer we need a reference to this spinner component okay so before we actually create our reference let's add some comments so here I will write below now we need to create a ref for that I will create a refs let's say called spinner [Music] and then I'm going to pass this ref to the spinner but just for the SQL Simplicity or wrap the spinner component inside of a div and then pass the ref to this the reason why I did this is because if you go to spinner company it doesn't accept any ref prop right we would have to create it so just for keeping things simple I'm just wrapping the spinner component by a diff and we're passing the ref to this diff itself now if I save the page you can come here and you can see that at the bottom we have our spinner component but for some reason this is coming slightly to the left so I think the reason is that we have to put this outside of the cards container not inside so let's cut and paste over here okay so now we have our spinner at the desired position so now let's instantiate the intersection of the server and we will do it inside of a use effect the reason which I will share in a while and then an empty array now it's an empty array but I will definitely pass some dependencies so we'll do that for now let's call our instantiate the induction Observer and to do that we have to do new intersection Observer and it accepts two parameters which we will also again pass in a while but it returns something called as Observer and now here what we can do is we can basically check that whether we have our Target element reference or not so to do that as we know that we have set our Target element inside of the spinner s so you can just basically check if spinner F dot current exist then we can do Observer dot observe our Target element which is again this one now the reason I used a use effect is that it makes it very easy to remove the Observer whenever our component unmounts and in order to do that we can just return Observer Dot and observe and again pass hours primaris dot correct so this Behavior makes it very easy to remove the Observer for from our Target development and prevent memory leaks that is one of the reason why we use by juice effect and then in the dependency array we can pass the spinner ref itself now coming to this intersection Observer as I said it accepts two parameters so first is the Callback which will again write in a while and the second is options so in the options you can specify the settings basically the settings of the intersection Observer so the options takes three parameters first is the root will pass it as null second one is the root margin which I will pass as 0 pixels and the third one is threshold which I will pass as one so now what are these three properties the first one is the root and since we passed null it will default back to this viewport and if we did not pass it as null and let's say pass it any reference to it then the parent would be in that HTML reference against which or you can say relative to which the intersection would have been calculated now coming to root margin root margin is basically margin around the parent and in our case since this is 0 pixels we're basically saying that we don't want any margin when we're actually calculating the intersection but you could of course pass it a margin similar to the CSS margin property now the last one is threshold so threshold value of 1 means that we want the intersection Observer to inform us when the target is 100 visible so 100 visible means one and suppose if you want the intersection Observer to inform you at 50 or you can say when the target is visible by 50 you can set this as 0.5 but since we want to be informed only when it is at 100 we have passed it as one okay so now moving to the Callback function which is the first parameter also I'm seeing some warning over here so to fix this morning what we have to do is we have to store the target element reference in a variable inside of the use effect so I'm going to name the variable as F and here we will store our Target element reference which is this now instead of using this we have to use our ref and now the warning is gone so now let's see what argument is actually passed to this callback function so I'm going to name it as entries and let's go into login respect element and you can see that this is an array and the first element is where all of the required information is present so now what we can do is we can directly extract this first element of the array by doing entry and then if we console log this entry variable we will be able to directly see our first element so inside of this our main point of interest is this is intersecting property and we will be using this to distinguish whether our Target element is visible or not so now let's store this value inside of a state itself and I'm going to name it as is intersecting the set is intersecting and the default value will be false and then just copy it and here we will do entry dot is enter second so now let's console log this is intersecting State itself to see whether we are getting the correct information or not refresh the page so now if we scroll down and when this spinner is visible we can see that we got a value of true and when it's hidden we got a value of false so this intersection Observer is working properly now we can use this information to fetch products whenever our Target element is visible one thing you all might have noticed is that we are always showing the spinner but we want this winner to be shown only when there are more products to fetch so how can we determine this so to determine this we can go to our Network Tab and if I refresh the page and make an API call we'll see that in the response we have something called as total so total indicates the number of products that we have in total and here at a time we're fetching only 10 products so now we can use this information to basically know when there are more products to fetch or when all our products has finished loading or fetching so to implement this I'm going to create one another state which I will name it as hash mode set has more and its default value will be false now if we go to our if statement which is this and here what we can do is previous and then inside here I will create a new variable called as new data and spread out the previous data and then add the new data which is the products that we have fetched and then we'll basically check whether this new data length is less than the total number of products which is this so if it's less than the total number of products it means that we have more products to fetch so in this case we'll set has more to true as set has more will be false also to reset our has more State you can add set has more false here also and inside of the catch block also okay so I forgot to return the new data and with this they should fix the error and then finally in the bottom what we can do is we can show the spinner only when there is more products to fetch so now if I refresh the page you can see that we're just seeing one spinner also there is a potential bug which you all might face so here what we're doing is we are unobserving our Target element whenever our component unmounts so we just have to add a check here which basically says that if we have the target element only then we want to unobserve it so this should fix a potential bug which you all might face when you write in this code so now we will Implement our own custom page logic so here in this API I will store the limit value inside of a variable and I will name the variable as limit itself and then use it over here now I will create one reference to store our page number so let's name it as page ref user F and its default page value will be 1. and now we will use this page ref inside here to create our own custom page launch also I want the page number to be passed as an argument to this fetch products function so let's create a parameter called page and its default value should be page ref dot current which means that if we do not pass any page number to disk fetch products function it will take the page value from here so now let's use this page over here okay so before that for the skip equal to 10 means it means that we want to skip the first 10 products and then fetch another set of 10 products so let's write some logic here I will write page minus 1 into limit so now what does this means so if we visualize it so let's say when we're initially fetching our products then the page number is one so one minus 1 will be 0 and then 0 into limit will be again 0. basically we're skipping zero products because we want the first set of 10 products now let's take the example of page two so when we pass page S2 what will happen is 2 minus 1 will be 1 and then 1 into 10 will be 10. so we will be then skipping the first 10 products similarly for page 3 we will be skipping the first 20 products and so on so with this we have implemented our own custom page logic and now we can use this page number to fetch whichever page we want to fetch so now if we go to this use effect where we are initially fetching our products I will pass one as the page value or even if you don't pass it will default back to one only so it totally depends on you that you want to explicitly pass the page number or not when we are initially fetching our products so this page number will be handy when we actually fetch more products so now let's write down the logic for the same and if we go down we also know that is intersecting indicates when now Target element is visible so we can use this to actually fetch more products when it is visible so to do that we have to write a use effect and here pass is intersecting as a dependency and inside of this use effect we have to do if it's intersecting which basically means that if it is visible then fetch more products but now here we have to pass the page number of the next page so in order to get the next page number we have to add 1 to the current page which we can do this in this way plus one and then we also have to update the current page number to the next one which we can again do right this way so now let's check whether this is actually working or not so if I scroll down you can see that our spinner is visible but this did not trigger any API call to fetch more products which means that there is an issue the issue is that this use effect to create an observer instance run even before our spinner ref got attached to this diff because of which this became null and we were not able to attach any listener to our Target element so to solve this issue what we can do is we can create one state let's name it as a spinner ref visible and I will set it as false and then below what we can do is instead of assigning the ref this way we can create a callback and this will get the reference itself let's name it SEL and then we'll attach this reference to our spinner F this way spinner F dot current equal to El after this we will flip the state that we just created so let's take the previous value and flip the state like this so the reason we're flipping the state is to trigger a change and now let's pass this inside the dependence array and since we are flipping the state here so whenever our reference gets attached this flipping of state will trigger the use effect to run again since your passing a spinner if visible State as a dependent because of which we will have our reference and everything will work as expected now let's save this and now if I scroll down please focus on this network tab as you can see as the spinner got visible we fetch page two of our products so if you give a closer look you can see that we're skipping the first 10 products and then fetching the next 10 products which technically means that we're fetching page two and again if I scroll down this time we're skipping 20 products and then again fetching the next set of 10 products which means that this is Page Three so this way if we keep on scrolling down it will keep on fetching more products but I think you have noticed a problem right and the problem is that we have seen two spinners over here so the first spinner is due to this so this spinner should technically be shown only when the products are fetched for the first time or when we make the first API call and the second spinner is this one itself so now we somehow have to hide this spinner when we are fetching more products so to do that what we can do is if we go to our fetch products function here we can add one another parameter let's say called is fetching first name and since we are setting the set is loading state to True over here what we can do is we want to set this is loading state to True only when we are fetching it for the first time so what I can do is is fetching first time and this similarly we have to do this in the finally block also and then in this use effect where we are actually fetching the initial set of products we can pass is fetching first time here so as we know the first param is the page number so we can pass it as one and here we can pass it as true so now if I save and refresh you can see that we are seeing only one spinner right okay let's close this and with this you can see that our infinite scrolling is working perfectly also to make it more robust what we can do is here when we're fetching more products we can also add that if as more is true and if our Target element is visible only then fetch more products we also have to pass this here in the dependency array and now if we again check this is working as expected so with this we have come to the end of our video and make sure to subscribe if you haven't and if you have learned something new gained some knowledge do like and comment on the video this gives me motivation to create more such videos for you all guys okay then see you until the next video Stay Safe Keep coding bye bye Keith coding bye
Info
Channel: FrontendInterviewPro
Views: 627
Rating: undefined out of 5
Keywords: infinite scrolling in react, intersection observer react, intersection observer javascript, mastering infinite scrolling, react projects
Id: dElrm_hwydI
Channel Id: undefined
Length: 29min 35sec (1775 seconds)
Published: Mon Sep 11 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.