Infinite-Scroll And GitHub REST API
Tags: javascript, react, tutorial, beginners
Published: 2022-11-22 21:45:29


## Table of Contents
1. [Introduction](#introduction)
2. [Retrieving Data Using GitHub REST APIs](#retrieving)
3. [Exploring The API With PostMan](#exploring)
4. [Setting Up A React Project](#setting-up)
5. [Loading The Data](#loading)
6. [Creating Components](#creating)
7. [How to Add The Search Input](#search-input)
8. [Implementing Loading Function and Infinite Scroll](#implementing)
9. [Adding Cool Search Filter](#search-filter)

<h2>
  <a name="introduction" href="#introduction" class="anchor"></a>
Introduction
</h2>

GitHub is a popular resource for developers to store and share their code. The REST APIs allow users to create, edit, and delete repositories using the HTTP protocol.

REST APIs are a type of software architecture that uses the HTTP protocol to communicate with an application. The application is referred to as a client and the REST API is the server.

REST APIs are most often used for web services. They are also commonly used in mobile applications, but they can be used in any application with a web browser interface.

In this tutorial, you'll learn about **Infinite scroll**, a useful UX technique in which content continually loads when a user reaches the bottom of the page. In addition to Infinite scroll, you’ll use the [Axios library](https://github.com/axios/axios/blob/main/README.md) to make API requests and process the obtained results. **Axios** is a great fit because it automatically transforms JSON data into JavaScript objects, and it supports Promises, leading to code that’s easier to read and debug. You'll get from this article information about the underlying concepts of [useEffect](https://reactjs.org/docs/hooks-effect.html). We'll also learn how to use the GitHub REST API to load and extract data from the GitHub API.

To crown it all, we will learn advance search bar which filters through some data and displays the information in a really nice way. We could search for specific github users from the Github REST API.

In this Project:
We'll use the Github api Via the https://api.github.com/. 

We'll implement the following:

- Get a list of the most starred github repo that was created in the last 30 days.

We are going to show the Following fields in our display:

- Repository name,

- Repository description.

- Number of stars for the repo.

- Number of issues for the repo.

- Username and avatar of the repo owner.

- Implement data pagination with infinite scroll.


This is what we will build:

{% embed https://photos.app.goo.gl/yXCWX2aqvw6Bw3WR8 %}

<h3>
  <a name="retrieving" href="#retrieving" class="anchor"></a>
  Step 1 — Retrieving Data Using GitHub REST APIs?
</h3>

GitHub REST API Documentation link: https://docs.github.com/en/rest/guides/getting-started-with-the-rest-api

Add the Base URL for the GitHub REST API, https://api.github.com/ to the path to get the full repository_search_url: https://api.github.com/search/repositories?q={query}&sort=stars&order=desc

<h3>
  <a name="exploring" href="#exploring" class="anchor"></a>
  Step 2 — Exploring The API With PostMan
</h3>

To get the most starred Github repos created in the last 30 days (relative to 2021-09-13), we'll need to call the following endpoint:
https://api.github.com/search/repositories?q=created:>2021-08-13&sort=stars&order=desc/github

The greater than sign `:>`
created: YYYY-MM-DD created:>2021-08-13 matches repositories submitted 30 days ago that were created after 2021.

We're going to use Postman [(download it here if you haven't gotten it)](https://www.postman.com/downloads/) to play with the API.

Navigate into your Postman and paste in the Base URL. Click "send" and you should get some JSON back in the "body" section like so:


![postman_image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wzljcqr9qoj6xwox3a6h.png)


<h3>
  <a name="setting-up" href="#setting-up" class="anchor"></a>
Step 3 — Setting Up A React Project
</h3>

Ok with that out of the way, we can now see the step-by-step implementation to create a starred-repo-app in Reactjs.

If you are new to React and you want to explore the language features and syntax then Create React App is the best choice for creating a new React application. By far it is one of the best and most widely used ways of creating single page applications. 

There are a couple of methods to create a new React app but you may choose: `the npx method`

npx comes with npm 5.2+ and higher, you can use the following command to create a new React project.

```javascript
npx create-react-app starred-repo-app
```

Once we run this command, a folder named "starred-repo-app" will be created where we specified on our computer and all of the packages it requires will be automatically installed.

Navigate into your directory and enter your main folder starred-repos-app as:

```python
cd starred-repos-app
```
Project Structure should look like the following once our project files have been created and our dependencies have been installed:


![project_structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yo372bounykn34azwr26.png)

Remove the default App.test.js, logo.svg, reportWebVitals.js, setUpTests.js you have already created.

Notice, you'll come across an error on your local server:

![error message](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/54lbwo1vssdoj43hzt6p.png)

A simple way to handle this error is basically to navigate to the `index.js` file and remove the index.css and reportWebVitals();

We are going to install React-icons which is a small library that helps you add icons (from all different icon libraries) to your React apps.

```python
npm i react-icons
```
Next, we're going to add a Circular Progress Bar from Material UI which is a popular web widget that shows progress along a circle. We use it to implement the loading function.

```python
npm install @mui/material @emotion/react @emotion/styled
```

When thats finished installing, we're going to add an Infinite Scroll Bar

An application that implements infinite scroll consists of a layout that allows users to keep consuming a certain amount of information without any pause, as the content is automatically loaded as the user scrolls.

There are a few different ways that you can implement infinite scroll in React.js. One way is to use a library like react-infinite-scroll-component. This library provides a component that will trigger an event when the user scrolls to the bottom of the page. You can then use this event to load more content.

To use react-infinite-scroll-component, you need to install it first using npm:

```python
npm i react-infinite-scroll-component
```
Then, you can import it into your React component.

Thats all we need, so go ahead and run the app:

```javascript
npm start
```

<h3>
  <a name="loading" href="#loading" class="anchor"></a>
Step 4 — Loading The Data
</h3>

We are going to make use of the useEffect Hook that allows us to perform side effects in our components.

Some examples of side effects are: fetching data, directly updating the DOM, and timers.

useEffect accepts two arguments, a function and a dependency array. The second argument is optional.

It is evident from the fact that we may sometimes in React applications need to get data from the external source. One way to make  such data to be visible on our website is using Axios in retrieving the data thereby adding it to the state to facilitate the application whenever the requirement arises.

In our application, we'll need to run this command to install Axios.

```python
npm install axios
```

At the heart of all React applications are components. A component is a self-contained module that renders some output. We are going to create interface elements like a button and an input field in our application.

<h3>
  <a name="creating" href="#creating" class="anchor"></a>
Step 5 — Creating Components
</h3>

We're going to create StarredUsers component to display the most starred Github repo that comes back in the search request.

Let's organize our project folders step by step:

- Create a new folder named components inside the src folder.

- Inside the components folder, make another folder called Commons. Inside Commons, create three files: **Button.js**, **Icons.js**, and **Input.js**.

- Now, go back to the components folder and create a new folder named **GithubStarredList**. Inside this folder, add two files: **GithubStarredList.js** and **GithubStarredList.css**.

- Inside the **GithubStarredList** folder, create another folder named **GithubStarredUser**. Inside it, add two files: **GithubStarredUser.js** and **GithubStarredUser.css**.


Open up Button.js and add the following:

```javascript
// Here we passed in a custom className, title, type onClick as props

const Button = ({ className, title, type, onClick = () => {} }) => {
  return (
    <button className={className} type={type} onClick={onClick}>
      {title}
    </button>
  );
};

export default Button;
```
React components use props to communicate with each other. Every parent component can pass some information to its child components by giving them props.

Open up Icons.js and add the following:

```javascript
//the prop icons was renamed to Icons to make it have a distinctive feature and easy to use.

const UserIcons = ({ icons: Icons, className, id, onClick = () => {} }) => {
  return (
//JavaScript Comparison using the && Operator (Logical AND)
    <>{Icons && <Icons className={className} id={id} onClick={onClick} />}</>
  );
};
export default UserIcons;
```

Open up Input.js and add the following:

```javascript
const Input = ({
  type,
  className,
  name,
  placeholder,
  onChange = () => {},
  value,
}) => {
  return (
    <>
      <input
        type={type}
        className={className}
        name={name}
        placeholder={placeholder}
        required
        onChange={onChange}
        value={value}
      />
    </>
  );
};
export default Input;
```

Before we start working on our main component, app.js, we need to make sure we can use absolute imports. To do this, we'll create a file named **"jsconfig.json."** in the root directory of our project.

Inside the jsconfig.json file, add the following code:

```javascript
{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "*": ["src/*"],
      "@utils/*": ["utils/*"]
    }
  },
  "include": ["src"]
}
```

Here in App.js we can easily import the components of Axios, as shown below. Open App.js, delete everything, and replace it with the following:

```javascript
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import './App.css';

const App = () => {
  const [githubResponse, setGithubResponse] = useState({
    total_count: 0,
    items: [],
    incomplete_results: false,
  });
// Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // execute side effect
    try {
      const fetchItems = async () => {
        const results = await axios.get(
          `https://api.github.com/search/repositories?q=created:>2021-08-13&sort=stars&order=desc/github`
        );
        console.log(results);
      };
      fetchItems();
    } catch (error) {}
    // optional dependency array
    // 0 or more entries
  }, []);

  return <div className='App'></div>;
};

export default App;
```

Here in App.js

We declare the **githubResponse** state variable, and then we tell React we need to use an effect. We pass a function to the useEffect Hook. This function we pass is our effect. Inside our effect, we use a basic React set up to fetch data from API using Axios. We can read the latest **githubResponse** inside the effect because it’s in the scope of our function. When React renders our component, it will remember the effect we used, and then run our effect after updating the DOM. This happens for every render, including the first one.

![logged_results](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yewtiy4cpx14mfm0npg3.png)

When we console log results we get a list of objects containing the data we are getting from the API inside the fetchItems function.

After receiving the data from the API we'll have  to change the data state from blank array to the response data using the React setState method. 

Navigate back to App.js and update it with the following:

```javascript
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import GithubStarredList from "components/GithubStarredList/GithubStarredList"; //We're importing the GithubStarredList component
import './App.css';

const App = () => {
  const [githubResponse, setGithubResponse] = useState({
    total_count: 0,
    items: [],
    incomplete_results: false,
  });

  useEffect(() => {
    try {
      const fetchItems = async () => {
//we destructured the data from results
        const { data } = await axios.get(
          `https://api.github.com/search/repositories?q=created:>2021-08-13&sort=stars&order=desc/github`
        );
        setGithubResponse((prev) => ({
          ...prev,
          ...data,
        }));
      };
      fetchItems();
    } catch (error) {}
  }, []);
//We're rendering the StarredUsers and passing githubResponse we stored in state as props
  return <GithubStarredList githubResponse={githubResponse} />;
};

export default App;
```

### Spread syntax (...)

When setting the React state with **setGithubResponse**, we used The [spread (...)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) operator syntax which allows us iterate through our prev state and response data.

You might want to use prevState: prev if the new state is computed using the previous state. In other words if you need to change your state based on the previous state.

Open `App.css` and add the following styles:

```javascript
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  background-color: #1f2937;
  height: 100%;
  font-size: 12px;
}

body::-webkit-scrollbar {
  display: none;
}
```

Now using the [map](https://www.w3schools.com/jsref/jsref_map.asp) function we'll iterate the data inside the render method of StarredUsers.

Open up GithubStarredList.js and add the following lines of code:

```javascript
import React from "react";
import SearchBar from "components/SearchBar/SearchBar";
import GithubStarredUser from "components/GithubStarredList/GithubStarredUser/GithubStarredUser";
import "./GithubStarredList.css";

const GithubStarredList = ({ githubResponse }) => {
  return (
    <div className="starred-users">
      <SearchBar githubResponse={githubResponse} />
      <div className="starred-user-list">
        {githubResponse?.items?.map((item) => (
          <GithubStarredUser item={item} key={item.id} />
        ))}
      </div>
    </div>
  );
};

export default GithubStarredList;
```
For more information about [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining).

## Adding Style

So after calling the GithubStarredList.js expression there is a need to style the component for showing the starred users, for that we have created the GithubStarredList.css in the GithubStarredList folder.

{% codepen https://codepen.io/willochs316/pen/yLEbyKz %}

Inside the **GithubStarredUser.js** file, add the following code:

```javascript
import React from "react";
import "./GithubStarredUser.css";

const GithubStarredUser = ({ item }) => {
  return (
    <div className="starred-user-content" key={item.id}>
      <div className="user-profile">
        <img
          src={item.owner.avatar_url}
          className="user-avatar"
          alt={item.owner.name}
        />

        <div className="user-profile-info">
          <h2 className="user-profile-name">{item.full_name}</h2>

          <p className="user-profile-description">
            {item.description}
            <span> {item.url}</span>
          </p>

          <div className="user-profile-ratings">
            <div className="user-profile-stars">
              Stars: {item.stargazers_count}k
            </div>

            <div className="user-profile-issues">
              Issues: {item.open_issues_count}k
            </div>

            <div className="user-profile-submission">
              <span className="user-submission-text">
                Submitted 30 days ago by {item.name}
              </span>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default GithubStarredUser;
```

Find the style for **GithubStarredUser.css** below;

{% codepen https://codepen.io/willochs316/pen/yLGJpGr %}

<h3>
  <a name="search-input" href="#search-input" class="anchor"></a>
Step 6 — How To Add The Search Input
</h3>

Add a new component to the components folder called SearchBar inside create a file SearchBar.js and Search.css. Open SearchBar.js and add the following:

```javascript
import React from "react";
import Button from "components/Commons/Button";
import Input from "components/Commons/Input";
import "./Search.css";

const SearchBar = () => {
  return (
    <section className="search-section">
      <div className="search-input-section">
        <Input className="search-input" placeholder="Search...." />

        <Button className="search-button" title="Search" type="submit" />
      </div>
    </section>
  );
};

export default SearchBar;
```

## Adding SearchBar Style

{% codepen https://codepen.io/willochs316/pen/eYKWmaj %}

Save and run the app, and then if you go to the browser you should see the following:

![Image_description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6nrxw6s9pmgkc8rbnutm.png)

<h3>
  <a name="implementing" href="#implementing" class="anchor"></a>
Step 7 — Implementing Loading Function and Infinite Scroll
</h3>

Now that we have gotten the first steps of retrieving the data and rendering that data, we need to implement the loading function so basically when we are fetching the data we should be able to see a little spinner comes up saying its loading and once that data comes in we get rid of the spinner and show the data.

Update **App.js** with the following:

```javascript
import React, { useEffect, useState } from "react";
import axios from "axios";
import GithubStarredList from "components/GithubStarredList/GithubStarredList";
import "./App.css";

const App = () => {
  const [githubResponse, setGithubResponse] = useState({
    total_count: 0,
    items: [],
    incomplete_results: false,
  });
  const [isLoading, setIsLoading] = useState(true);
  const [isError, setIsError] = useState(false);
  const [currentPage, setCurrentPage] = useState(1);

  useEffect(() => {
    try {
      const fetchItems = async () => {
        setIsError(false);
        currentPage === 1 && setIsLoading(true);
        const { data } = await axios.get(
          `https://api.github.com/search/repositories?q=created:%3E2021-08-13&sort=stars&order=desc/github&page=${currentPage}`
        );
        setGithubResponse((prev) => ({
          ...prev,
          ...data,
        }));
        currentPage === 1 && setIsLoading(false);
      };
      fetchItems();
    } catch (error) {
      setIsError(true);
      console.log(error.message);
    }
  }, [currentPage]);

  const loadMore = () => {
    githubResponse.incomplete_results && setCurrentPage((prev) => (prev += 1));
  };

  return (
    !isError && (
      <GithubStarredList
        isLoading={isLoading}
        githubResponse={githubResponse}
        loadMore={loadMore}
      />
    )
  );
};

export default App;
```

Update **GithubStarredList.js** with the following:

```javascript
import React from "react";
import InfiniteScroll from "react-infinite-scroll-component";
import CircularProgress from "@mui/material/CircularProgress";
import { Box } from "@mui/material";
import SearchBar from "components/SearchBar/SearchBar";
import GithubStarredUser from "components/GithubStarredList/GithubStarredUser/GithubStarredUser";
import "./GithubStarredList.css";

const GithubStarredList = ({ githubResponse, isLoading, loadMore }) => {
  const onNext = () => {
    loadMore?.();
  };

  return isLoading ? (
    <Box className="progressBar">
      <CircularProgress />
    </Box>
  ) : (
    <div className="starred-users">
      <SearchBar githubResponse={githubResponse} />

      <InfiniteScroll
        dataLength={githubResponse?.items}
        next={onNext}
        hasMore={githubResponse?.incomplete_results}
        loader={
          <Box className="progressBar">
            <CircularProgress />
          </Box>
        }
      >
        <div className="starred-user-list">
          {githubResponse?.items?.map((item) => (
            <GithubStarredUser item={item} key={item.id} />
          ))}
        </div>
      </InfiniteScroll>
    </div>
  );
};

export default GithubStarredList;
```

<h3>
  <a name="search-filter" href="#search-filter" class="anchor"></a>
Step 8 — Adding Cool Search Filter
</h3>

A search filter is a specific attribute a user can use to refine the search results of a particular product listing page.

Update **SearchBar.js** with the following:

```javascript
import React, { useState } from "react";
import Button from "components/Commons/Button";
import UserIcons from "components/Commons/Icons";
import Input from "components/Commons/Input";
import { FaTimes } from "react-icons/fa";
import "./Search.css";

const SearchBar = ({ githubResponse }) => {
  const [filteredItems, setFilteredItems] = useState([]);
  const [wordEntered, setwordEntered] = useState("");

  const handleFilteredItems = (event) => {
    const searchWord = event.target.value;
    setwordEntered(searchWord);
    const newFilteredItems = githubResponse?.items?.filter((item) => {
      return item.html_url.toLowerCase().includes(searchWord.toLowerCase());
    });

    if (searchWord === "") {
      return setFilteredItems([]);
    } else {
      setFilteredItems(newFilteredItems);
    }
  };

  const clearInput = () => {
    setFilteredItems([]);
    setwordEntered("");
  };
  return (
    <section className="search-section">
      <div className="search-input-section">
        <Input
          className="search-input"
          value={wordEntered}
          onChange={handleFilteredItems}
          placeholder="Search...."
        />
        {filteredItems.length === 0 ? (
          <Button className="search-button" title="Search" type="submit" />
        ) : (
          <UserIcons
            icons={FaTimes}
            className="clear-button"
            onClick={clearInput}
          />
        )}
      </div>

      {filteredItems.length !== 0 && (
        <div className="search-results">
          {filteredItems.slice(0, 5).map((item) => (
            <a
              className="search-result-item"
              href={item.html_url}
              target="true"
              key={item.id}
            >
              <p>{item.full_name}</p>
            </a>
          ))}
        </div>
      )}
    </section>
  );
};

export default SearchBar;
```

## Follow me

|  |  |  |  |
| --------- |:---------:| -------:| -------:|
| [![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bp70sifnip8wuupc49r7.png)](https://dev.to/willochs316) | [![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/43w3ga66hfvhmyg13f5l.png)](https://github.com/Willochs316) | [![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ftky33oi4pkxktvleic.png)](https://www.linkedin.com/in/willochs316/) | [![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3am6nfvit6tlnsss5rvp.png)](https://twitter.com/willochs_dev) |