ByteSize: Dynamic Search With Flask + React

Over the last few articles we’ve been going through how to create an infinite scroll image grid using Flask, React and the Unsplash API. In this installment, we’re going to be adding the ability to search for photos to our component. If you want to catch up, you can find them here:

  1. Setup and Infinite Feed
  2. Infinite Grid and Styling

The repository for this installment can be found here if you wanted to follow along!

Photo by Paul Skorupskas on Unsplash

Backend

The Unsplash API gives you functionality to search their image collection with natural language. That seems like a piece of functionality that we should pass on to our users. Since we already have our grid all set up and ready to go, we just need to set up the request on our backend endpoint to pass along to Unsplash and return our images.

Since the endpoint for image search works differently from the general one, we will also need to create a conditional flow in order to properly route them. I check for the argument and check to see that it doesn’t equal null before sending the request. Once we know, we can append the user query to the query object to be sent in the request, and format the returned response appropriately (in app/main/views.py).

Front End

The backend was a simple fix, but our front end is going to need a little bit of work to get it working properly.

The first step is adding an input field.

We can use Material-UI to import a styled text field for the search bar, as well as some decorations for the component. We’ll start off by importing them at the top of our InfiniteScroll file (in app\static\src\InfiniteScroll.jsx).

Since we’ll want the search bar to appear over top of the Infinite Grid, we will add a parent div containing both of them to the component. We will also need to create an input state to hold the users entry.

In order to maintain control (and avoid a scary looking error message) of the TextField, we provide a conditional for the value field as an OR statement with our input state.

We then add an ImageSearchIcon to the end of our search bar with InputProps by using the InputAdornment component. I find that this adds a little aesthetic decoration to what can be a rather boring element. We will also use handleChange to manage user input into the element and update the input state.

Debouncing

Since we don’t want a request to be firing off every time a user enters or deletes a character, we’ll need to “debounce” the input. This works by putting a timer between the entry and when the function is updated, allowing you to wait a few moments to let the user finish typing before performing the query. This prevents your server from getting overloaded in an avalanche of requests, and bogging down your users from images being continuously rendered.

We will create the debounce function as a hook, and reference it in our InfiniteScroll.jsx file to keep things clean and separated.

For this, the value is the value you are seeking to debounce, and the delay will indicate the length of time before the value is returned.

Then we’ll import useDebounce within InfiniteScroll.jsx in order to use it.

Query Function

Now we can put together the query for our backend.

First thing we’ll have to do is add the debounced query to our query string in agent.jsx (in app/static/src/agent.jsx)

While we will be able to use the fetchData function for subsequent requests (as long as we’re providing the debounced query with them), we will need to create a new function to retrieve the first request of a user query. This is because we don’t want our search to simply append to the end of the already returned image list, but rather to generate an entirely new one.

AutoFire

We will also need to make sure that our function fires once the debounced input is populated. Instead of using a clicked button to fire the event, we can simply monitor changes to debouncedQuery to trigger the query with useEffect.

Clean Slate

Now that we can search with the user input automatically, we will want to make a way that reverts our grid back. We can use a button to clear the data, however if we want to fetch the feed we will need to call either fetchData or fetchQuery, or create an entirely new function altogether. We don’t want the new feed to once again be appended to the end of the search, but be built from from scratch. As fetchQuery already provides this functionality, we can use a ternary statement in the Images.get() request using a default parameter in order to distinguish between the two uses. We just need to create our button, and trigger it with an onClick function.

For a bit of cleanup, we’ll also implement a bit of text to let our users know if there are no results found from their search.

Styling

With that, our search bar is working!

We still need to do a little bit of cleaning up for styling. The first is filling out some of our classes in the InfiniteScroll component (in app/static/src/InfiniteScroll.jsx)

Finally, we’ll change up some of our theme styles to adjust the coloring of the active TextField to something a little more subdued by using the MUI ThemeProvider (in app/static/src/App.jsx)

As one final element as well, our current site doesn’t recognize its to adjust for mobile screens. This is a remarkably simple step by adding a single meta tag to our index.html file (in app/static/templates/index.html)

Success!

And with that we’re done!

As always thank you for reading, be sure to check back next time as we’ll start getting into optimization methods to make your component lightning fast!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store