- Read Tutorial
- Watch Guide Video
I'm sure you've seen on websites or mobile applications where you have a little spinning icon as your data's coming in, it's spinning and that tells you that the application is working.
I think this is something that is important to build into our infinite scroll feature because one, it's a cool skill to learn, you're definitely gonna have to build that out. It's a very common practice to add a loading icon as a spot on your app so that users know that something's happening.
It'd be weird to just have a blank page or a blank widget or something like that. So one, it's a good skill to learn and you'll be able to populate what we're gonna do here in any other application. But also when it comes to an infinite scroll feature, if you do not have some way of telling the user that there are more posts so when they scroll all the way down to the bottom of the page, what I want is to have a little icon here that starts spinning to give them some feedback to say hey, we have some more records and we're working on getting them for you right now. So that is why we're going to integrate that.
So let's start, this is going to take a few steps. We're gonna have to create a few files and the main reason is because what I wanna do is I don't want to just this into the blog page. I want to build a component or I should say an element and some styles, that could be used in any other application or anywhere else on the page that we might want. So that is why we're gonna build this with a level of generality and abstraction.
So let's start, I'm gonna open up the blog component here and the first thing we need to do is to keep track of our state to see if we are in a loading state or if we're not. So I'm gonna add a new state item here, I'm just gonna call it isLoading
and when this component mounts, we are going to be in a loading state. So I'm gonna set this to true by default and then we'll see where we need to update that.
So I'm gonna scroll now all the way down into our getBlogItems function and we are gonna be done loading whenever we get our response back. So this is gonna be the best place to change this state so I'll change this to say isLoading and we'll set it to false and that should be all that we need in order to update that.
blog.js
.then(response => { this.setState({ blogItems: response.data.portfolio_blogs, totalCount: response.data.meta.total_records, isLoading: false }); })
So now that we have our data and we know exactly when the page is loading and when it isn't, now we need to bring in the icon. So let's open up the app component and the icon that we're gonna want is called the spinner icon, and if you open up Google Chrome and just go to fontawesome.io, then you'll be able to see this.
I use the spinner icon so much that eventually you just kinda memorize a few of the key names, but if you type spinner here you'll see there are actually a few different options. I'm gonna use the basic spinner, but you could also use some of these other ones as well, it's completely up to you.
So I'm going to bring this in the same way we brought in all of our other icons. So near the top of the file, I'm gonna say faSpinner and then I'm going to add that to the library as well. So make sure you're adding all of those in,
app.js
import { faTrash, faSignOutAlt, faEdit, faSpinner } from "@fortawesome/free-solid-svg-icons";
library.add(faTrash, faSignOutAlt, faEdit, faSpinner);
and just in case, one question you may have is it kind of feels a little dirty to list out every icon that we want in the application in our app file. This is one of our most critical files. This is where we're defining routes and it has a number of very important responsibilities and this kind of feels wrong and really it is.
So what we're gonna walk through in a later section where we add some best practices and some refactors, I'm gonna show you how we can create a helper function and helper file where we can put all of this data just in one file, manage it from there and then all we have to do is import it and then call it from our app file. So don't worry if this feels kinda weird to do all of this right now, we are gonna clean that up.
So now that we have imported that, let's come into our blog component here and I'm going to import FontAwesomeIcon so I'll say import and this one, yes, this one is with curly brackets so I'll say FontAwesomeIcon from and this is that weird one. Let me actually look up at the app component and let me bring this one in, okay, so yeah, this is the one we want, at fortawesome/react-fontawesome.
app.js
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
Never feel bad if you do not remember every little name, especially for some of the dependencies like that. So now that we have this FontAwesomeIcon, now we can actually use it. So let's come down into our JSX code and for right now I'm going to just put it above all of the records. We're gonna add a ternary operator to check to see if the content is loading or if it's not later on, but for right now let's just make sure this is working.
So I'm going to create a div here and then inside of it's where I'm gonna put the call to the FontAwesomeIcon component. So here we're gonna call icon and this one's just called spinner and then let's close it off, hit save and come into Chrome and you can see it's right there, that's perfect, that's exactly what we're wanting.
blog.js
return ( <div className="blog-container"> <div> <FontAwesomeIcon icon="spinner" /> </div> <div className="content-container">{blogRecords}</div> </div> );
So now that we have this, let's see how we can make it spin. There is a prop you can pass to the FontAwesomeIcon component called spin and you can add it just like that,
<FontAwesomeIcon icon="spinner" spin />
and now if you hit save and go back to the browser, you'll see that it is spinning, so that is looking really good.
The next thing I wanna do is I wanna add some styles to that, this is a little bit tiny and it is not the color that I want it to be. So I'm going to open up our main style file, our import file and let me just create a new file call here called loaders and this is where we can put any and all styles for the loaders.
main.scss
@import "./variables.scss"; @import "./mixins.scss"; @import "./base.scss"; @import "./forms.scss"; @import "./button.scss"; @import "./grid.scss"; @import "./navigation.scss"; @import "./portfolio.scss"; @import "./portfolio-manager.scss"; @import "./portfolio-form.scss"; @import "./portfolio-sidebar.scss"; @import "./auth.scss"; @import "./blog.scss"; @import "./loaders.scss";
Now we're only gonna have one, but I'm going this because I want to show you exactly the way I'd build out an application and an application that even gets larger than this one.
So now that we have this I can hit save in the main style file, close it out and let's add a className to this div in blog.js. So here I'm going to say className and let's just call this our content-loader.
blog.js
return ( <div className="blog-container"> <div className="content-loader"> <FontAwesomeIcon icon="spinner" spin /> </div> <div className="content-container">{blogRecords}</div> </div> );
Then inside of our loaders file let's give this a few custom styles. Let's say that we want the font size to be double the normal size and then I want the color to be our teal.
loaders.scss
.content-loader { font-size: 2em; color: $teal }
So hit save here and come back and you can see the spinner now looks a lot better, at least I think so, you can style this however you want to.
So now that we have all of this in place, we do not want this div showing up. We don't want this spinner showing up alongside the blog records. We only want this to show up when content is loading so we want to place it below the blog records and then that is how we're gonna be able to implement it with infinite scroll.
So if you imagine this spinner right here and if you imagine that it is gonna be loading here at the bottom every time the user gets to the bottom of the page and that infinite scroll is activated.
So first and foremost I'm going to move all of this down below the blog records. So I'm gonna take this content-loader, I'm gonna put it below the blog records and I'm gonna show you why I'm changing this order here right now. If you hit refresh, you see how the loader is working right there, and it's still at the bottom it didn't actually go away.
But you can see that we don't have to call it twice, so I want to kinda have a dual purpose for this little spinner. I want it to show up when records are coming in so the first time a user comes to the page and we go out and we get those first 10 records, I want it to show up then right here, but I also want to use it whenever we are activating the infinite scroll again.
The way that we can use it with this dual purpose is just by placing it below the blog records and as soon as they come in, it's gonna push everything down. So this is giving us what we need, but now we do not want this showing up unless the content is loading.
So let's see how we can do this and we're gonna use a ternary operator for it, so I'm going to in curly brackets say this.state.isLoading so I'm gonna say if that is true, then I want you to show this loader, and I'm gonna use a multi-line ternary operator right here so I'm going to also wrap it inside of parens so I'm going to do that and then follow it up with a colon and then if the loading, isLoading is false, then I just want to say null is what gets returned.
blog.js
return ( <div className="blog-container"> <div className="content-container">{blogRecords}</div> {this.state.isLoading ? ( <div className="content-loader"> <FontAwesomeIcon icon="spinner" spin /> </div>) : null} </div> );
Okay, let's hit save here and let's see if this is working now. So our expected behavior is that our isLoading is gonna be updated to false as soon as records come in. So if we come back here, if I hit refresh, you can see our loader is going, it retrieves the records and then we come down here, you can see that it's disappeared just like it's supposed to.
As we continue to build this feature out, when I come down here and I will come to the very bottom of the page, if there are more records to go get, then we're gonna trigger isLoading to be true again and it's gonna pop up here at the very bottom of the content.
So great job if you went through that. I know that was a little bit of a longer guide, but you now know how to build and work with a loader in React.