This article delves into building a React-based image search app with infinite scroll. Users can search for images based on a query, and as they scroll down, additional images are fetched and shown. The Unsplash API is employed for searching and retrieving these images, resulting in a fully operational image search app with an integrated infinite scroll feature by the end of the tutorial.
Prerequisites:Approach to create Image Search App:Utilizing the Unsplash API, we’ll retrieve images according to the user’s search input. Implementing a debounced search input ensures delayed API requests during typing pauses. Additionally, an infinite scroll feature loads more images as the user reaches the page’s bottom, accompanied by a preloader spinner. The state management and required actions are facilitated through the useState, useEffect, and useRef hooks.
Steps to Create React App and Installing modules:Step 1: Create the project file using the command:
npx create-react-app <<Name_of_project>> Step 2: Navigate to the folder using the command
cd <<Name_of_project>> Step 3: Install the following packages
npm install unsplash-js lodash react-spinners Modules:
- unsplash-js: to interact with Unsplash this API.
- lodash: to use debounce utility function.
- react-spinners: to show spinner while fetching images.
Project Structure:
 project structure The updated dependencies in package.json file will look like:
"dependencies": { "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", "react-spinners": "^0.13.8", "unsplash-js": "^7.0.18", "web-vitals": "^2.1.4" } Note: Get your Unsplash API key from https://unsplash.com/
Example: Write the following code in respective files.
- App.js: This file contains the main logic.
- App.css: This file contains the styling.
CSS
/* App.css */
img {
max-width: 100%;
}
JavaScript
// App.js
import "./App.css";
import { useState, useEffect, useRef } from "react";
import { createApi } from "unsplash-js";
import { debounce } from "lodash";
import { BounceLoader } from "react-spinners";
const unsplash = createApi({
accessKey: << use your API Key>>,
});
function App() {
// State variable to store the search phrase
const [phrase, setPhrase] = useState("");
// Ref to hold the current value of the search phrase
const phraseRef = useRef(phrase);
// State variable to store the fetched images
const [images, setImages] = useState([]);
// Ref to hold the current value of the fetched images
const imagesRef = useRef(images);
// State variable to indicate if images are being fetched
const [fetching, setFetching] = useState(false);
// Ref to hold the current value of the fetching state
const fetchingRef = useRef(fetching);
function getUnsplashImages(query, page = 1) {
setFetching(true);
fetchingRef.current = true;
return new Promise((resolve, reject) => {
unsplash.search
.getPhotos({
query,
page,
perPage: 5,
})
.then((result) => {
// Update fetching state to indicate
//that images fetching is completed
setFetching(false);
fetchingRef.current = false;
resolve(result.response.results.map((result) =>
result.urls.regular));
});
});
}
useEffect(() => {
phraseRef.current = phrase;
if (phrase !== "")
debounce(() => {
setImages([]);
getUnsplashImages(phrase, 1).then((images) => {
setImages(images);
});
imagesRef.current = images;
}, 1000)();
}, [phrase]);
function handleScroll(e) {
const { scrollHeight, scrollTop, clientHeight } =
e.target.scrollingElement;
const isBottom = scrollHeight - scrollTop <= clientHeight;
if (isBottom && !fetchingRef.current) {
getUnsplashImages(
phraseRef.current,
imagesRef.current.length / 5 + 1
).then((newImages) => {
imagesRef.current = [...imagesRef.current, ...newImages];
setImages(imagesRef.current);
});
}
}
useEffect(() => {
document.addEventListener("scroll", handleScroll, { passive: true });
return () =>
document.removeEventListener("scroll", handleScroll);
}, []);
return (
<div>
<input
type="text"
value={phrase}
onChange={(e) => setPhrase(e.target.value)}
/>
<br />
{images.length > 0 && images.map((url) =>
<img src={url} />)}
<div>
{fetching && (
<div style={{ textAlign: "center" }}>
<BounceLoader speedMultiplier={5}
color="#000000" />
</div>
)}
</div>
</div>
);
}
export default App;
Steps to Run the Application:
Step 1: Type the following command in the terminal of your project directory
npm start Output: Type the following URL in your web browser http://localhost:3000/
|