Flask + React Infinite Scroll
Implementing Full Stack Infinite Scroll with React Hooks and Flask
Infinite scroll is an awesome way to promote stickiness for your application. Rather than having users click their way through a paginated list, having a natural flow helps your users see more content and spend more time on your site.
Infinite scroll can be tricky to implement correctly, and from my personal experience there’s not all that much info on developing with the Flask and React stack, which is a shame. If you are trying to get a lean MVP up and running quickly, I find that there’s almost no better combination.
For this project we will be using the Unsplash API to load a (seemingly) infinite series of images. Rather than simply calling the API directly from the front end, I am going to take a little more time with a dedicated back-end for two reasons:
- Protecting your API key for a publicly facing application.
- To show more about how to use Flask and React together and give insight on real world project development.
This tutorial will be higher level in creating a Flask and React apps, but if you’re looking for more introduction on that end please check out my Flask + Stream tutorial series.
To follow along you will also need a Client ID from Unsplash in order to load the pictures which you can get here.
Be sure to check out the repo
Backend
Our first task is setting up our backend. We will be using a basic Flask application using the Application Factory design pattern. Getting into a habit of using this structure will help when you get into larger project design and implementation, so the repetition is important.
Project Sandbox
We’ll start by creating a new project folder (mine is named InfiniteScroll), initializing a blank virtual environment (venv) and installing Flask.
After that, create an app directory and init file (app/__init__.py). This directory will hold the endpoints for our project as well as the function to initialize the application.
For our Flask initialization we have changed the default static and template folders. We do this in order to keep the complexity from having both Flask and React apps partitioned from one another. The aptly named create_app function takes the configuration name as an argument to initialize a Flask application with the given config variables. We will define these configurations next (in config.py):
The config file allows you to separate out different variables dependent on the deployment of your application. When we are developing in production, we don’t want to be using the development database, and vice versa. This way allows you to launch your application with the correct configuration for your deployment.
Application
Next up is our application file. This will use the create_app function we defined in our app directories __init__.py file (in application.py)
After that we will be creating our views before moving on to creating the base of our React application. Yet again we will be separating out our code to keep things organized and readable. In the app directory, create a subdirectory named main, along with an __init__ file and a views file. (in app/main/__init__.py)
Next, our views file (in app/main/views.py)
At this point we have referenced three things that don’t exist yet:
- The templates folder
- The static folder
- The index.html file
But Why?
If you’re coming from a Jinja2 HTML/CSS/JS framework, this new form of development can be really daunting. Why do we have to make twenty different directories before I can even run a “hello world” page?
Because.
The fact is that this structure is best for larger projects. If you were just trying to do a single page with infinite scroll, vanilla HTML/JS would be perfect. The problem is that any project you’re going to be doing will be a lot more complicated than that, with both front-end and backend logic that needs to implemented. React is great in that it offloads some of the logic from the backend, and doesn’t force you to reload the page every single time you update something. It’s also great because you can get into a whole host of optimization patterns that make rendering your page a lot quicker than any other way.
Rant Over
With that out of the way, let’s get our React app up and running. Starting off, let’s create those static and templates directories. The static folder will be in the top level of the app directory, and the templates directory will be within static. Finally, we create an index.html file in that templates directory which will be what we are returning as our index route request.
Finally, the script tag will render the React app that we will be creating next.
Front End
Navigating to the static directory in the command line (cd app/static), we will now initialize the React project with npm init. This creates the node_modules directory that will hold our project dependencies. Personally, I prefer using webpack for the same reason that I like using Flask, rapid iteration. I find it allows me to have a greater degree of customization than create-react-app, which in my experience requires a lot of pruning for smaller projects.
Installing Webpack in the command line with npm i webpack — save-dev (two dashes*)
Once we install this element, create a webpack configuration file in your static directory (in app/static/webpack.config.js):
Next we will update our package.json file that was created with npm init
To speed up the process you can simply run npm install after updating your package.json file rather than individually installing each dependency.
Finally, we’ll need a .babelrc file to read our JSX.
Setup Done, Main Project
Now that we are done setting up our React project, we can get into the Front-End development. Creating a src folder in our static directory, create an index.jsx and App.jsx file.
app/static/index.jsx
app/static/App.jsx
If you were to run both your Flask and React apps (with set FLASK_APP=application.py then flask run, and npm run watch respectively) and navigate to localhost in the browser, you will see “Hello World!” on a blank page. Your app is officially working!
Now we can actually make our infinite scroll. We’ll start off with retrieving the images.
First things first, we need to install the requests library, which we do in the main project directory with pip install requests.
Next, we will create our route.
This takes the page number argument, and creates a request to Unsplash for the images. We have given a config variable for the results per page, as well as the client ID. Abstracting away these things to config variables let’s you keep your sensitive information (like API keys) hidden from any Version Control system that your using, distributing a .env file that will be ignored by a .gitignore file. Additionally, when you have multiple routes that use the same general format, you can systematically alter the variables across multiple functions/routes without recoding everything by hand. We’ll create our .env file and .gitignore file now.
/.env
To keep things hidden from Version Control, add them as entries in a gitignore file:
/.gitignore
Python doesn’t natively know to read the .env file, so we will also need to install a module to help with it, python-dot. Install it with pip install python-dot.
Now we need to update our config file for these new variables (in config.py)
With our backend endpoint ready, our next task is to create a method to request it from the frontend. We will create an agent.jsx file to handle our requests (in app/static/src/agent.jsx):
While you can use a conventional API root variable, I personally prefer this method as it avoids issues with CORS when switching between local development and production.
The Main Course
Finally we’ll get to the meat of the project, the infinite scroll.
We will want to create functions that fire a request our back end for images when a user first opens the page, as well as when they scroll to the bottom of the page. We also will need to increment the page on each subsequent request to the backend. We also need to make sure that a request doesn’t fire while one is already in progress. We will trigger these events with useEffect, and adding an event listener for the scroll motion. Finally, we will add a loading module courtesy of React-Loading.
Adding state for the retrieved data, we map through it to display the images. If there is no data available we have a ternary statement to display the loading animation instead. We’ve also added another conditional to display the loading animation while another request is pending.
Finished
After that, we’re done!
Run both the Flask + React apps in the command line and check out your work:
Thanks for reading and be sure to check back because I’ll be going through integrating different styling methods for our infinite scroll, including a great Pinterest-style grid layout and swipeable cards a la Tinder.