How I Built A React App (myGithub) That Uses The GitHub API To Fetch Users Portfolio And Repos
Tags: react, api, github
Published: 2023-01-10 19:43:02

## Introduction
My Github is a React app built by implementing an API fetch of my Github Portfolio and repos. it allows users to easily search for their favorite repos on my profile. recently i added a search feature where users can also search for other github users profile and Repos.

## Aplication Overview
in order to complete this project i was able to achieve the following:

- Implemented an API fetch of my GitHub Portfolio showing a page with a list of all your repositories on GitHub

- Implemented **pagination** for the repo list

- Created another linking page that shows the data for a single repo clicked from the list of repositories using **nested routes**.

- Implemented proper SEO, Error Boundary (showing a page to test the error boundary), and 404 pages.

- Additionally i added search bar from which users can search for other github users.

## Getting started
**The tools for local development is:**

- Node

- Node Package Manager (npm)
To get started these tools must be installed on your computer. click [nodejs.org](https://nodejs.org/en/) to download them

## The Process
Firstly, after setting up my environment, I created a new React app by first creating a folder called myGithub and then running the following command in my terminal:

```
npx create-react-app .
```
This will create a react app here or in the current directory.  without creating a whole other folder within the folder, then `npm start` to run my application

**How I implemented the API fetch**
I started this project by implementing an API fetch of my GitHub portfolio in Home.jsx. By first Importing useEffect and useState from react.

```
import { useState, useEffect } from "react";
```
Next, I created two useState to store the responses from the API fetch. one to store the api response from fetching the user profile data and the other to store the response from fetching the repos

```
    const [user, setUser] = useState({});
    const [repos, setRepos] = useState([]);
```
i created a variable `url` to store the API url and `token` to store my github generated API key that had already been hidden in the `.env.local` file.
```
  const url = "https://api.github.com";
  const token = process.env.REACT_APP_GITHUB_API_KEY;
```
After that i created 2 asynchronous function called `getProfile` and`getRepo` that made the API request to the GitHub API to fetch the user profile data and repo.

```

 // fetching MY PROFILE
    const getProfile = async () => {
      const response = await fetch(`${url}/users/ble-syn`, {
        headers: {
          Authorization: `token ${token}`,
        },
      });
      const data = await response.json();
      setUser(data);
      setLoading(false);
    };

  // fetching MY REPOS 
  const getRepo = async () => {
    
    const response = await fetch(`${url}/users/ble-syn/repos`, {
      headers: {
        Authorization: `token ${token}`,
      },
    });
    const data = await response.json();
    setRepos(data);
    setLoading(false);
  };
```
I called `getProfile` and`getRepo` inside of a useEffect hook, and passed in an empty array as its dependency, so that the application makes the fetch request only once when it loads.

```
 useEffect(() => {
    getProfile();
    getRepo();
  }, []);
```
Since the `user` state created earlier is an object, I destructure it with dot notation to extract the data and render them in Githubprofile.jsx.

```
          <img className="profile-img" src={profile.avatar_url} alt="" />
          <h2>{profile.name}</h2>
          <p>{profile.login}</p>
          <p>{profile.bio}</p>
```
_N/B it is called **profile** in the code above because i created a seperate component called GithubProfile.jsx and passed the `user` state created in the home.jsx as a [prop](https://reactjs.org/docs/components-and-props.html) where i named it **profile**_

also, since the `repos` state  created too is an array of objects. I iterated through repos using the JavaScript map() method, destructured the object to extract the data and render them in repos.jsx.

**Implementing Pagination**
Pagination is used to break up a block of content into navigable pages on a webpage. Where a user can use links such as "next", "previous", and page numbers to navigate between pages

Aside from making it easier to see information, it also helps when loading data from a server. Using pagination, you can decide to only load a fixed number of items each time when the user decides to see them. This helps save time and avoid information overload on a page.

I implemented pagination using useState by first creating 2 states `currentPage` which i gave an initial state of **1** and `repoPerPage`, which specified the number of items that will be displayed on each page.
```
 const [currentPage, setCurrentPage] = useState(1);
 const [repoPerPage] = useState(4);
```


```
  // Pagination logic
  const indexOfLastNumber = currentPage * repoPerPage;
  const indexOfFirstNumber = indexOfLastNumber - repoPerPage;
  const currentRepo = repos.slice(indexOfFirstNumber, 
  indexOfLastNumber);
  const numberOfPages = Math.ceil(repos.length / repoPerPage);
```

```
function Pagination({ numberOfPages, currentPage, setCurrentPage }) {
  const [disabledPrev, setDisabledPrev] = useState(true);
  const [disabledNext, setDisabledNext] = useState(true);

  const pageNum = [...Array(numberOfPages + 1).keys()].slice(1);

  const nextPage = () => {
    if (currentPage !== numberOfPages) setCurrentPage(currentPage + 1);
  };
  const prevPage = () => {
    if (currentPage !== 1) setCurrentPage(currentPage - 1);
  };

  useEffect(() => {
    if (currentPage > 1) {
      setDisabledPrev(false);
    } else {
      setDisabledPrev(true);
    }

    if (currentPage === numberOfPages) {
      setDisabledNext(true);
    } else {
      setDisabledNext(false);
    }
  }, [currentPage, numberOfPages]);
  return (
    <div>
      <section className="pagination">
        <div onClick={prevPage} className={disabledPrev ? "disabled" : "prev"}>
          <LeftArrow />
          <p> Previous</p>
        </div>
        <div className="pagination-num flex">
          {pageNum.map((num) => (
            <div key={num}>
              <p
                onClick={() => setCurrentPage(num)}
                className="pagination-child"
              >
                {num}
              </p>
            </div>
          ))}
        </div>
        <div onClick={nextPage} className={disabledNext ? "disabled" : "next"}>
          <p>Next </p>
          <RightArrow />
        </div>
      </section>
    </div>
  );
}

export default Pagination;
```
**Implemention React-router, Nested Routes and Error Boundary** 

Firstly i installed the necessary dependencies needed to implement react-router, nested routes and error boundary by running the following commands in the terminal:

```
npm install --save react-router-dom
npm install --save react-error-boundary
```
- react-router-dom: used for routing pages in the app
- react-error-boundary: for handling errors that occur within React components.

For React Router I created 3 routes which include,  the home page(which has the nested routes),the error page, not found / 404 page.

Then I imported BrowserRouter from react-router-dom.

```
import { BrowserRouter as Router, Routes, Route} from "react-router-dom";
```
The outer component is Routes which wrapped the 3 Route component

The Route should have the path and element attributes, the path for the **404 page** is an asterisk.

The path attributes take in either the absolute or relative path in the string.
The element attributes take in the component that you want to render on the path accustomed to the Route.
 
check out the code below:
```
<Router>
      <Routes>
        <Route path="/" element={<Home />}>
          <Route path="/repo/:id" element={<RepoPage/>} />
        </Route>
        <Route path="/errortestpage" element={<ErrorTestPage />}/>
        <Route path="/not-found" element={<NotFound />} />
        <Route path="*" element={<Navigate to="/not-found" />} />
      </Routes>
    </Router>
```
**Nested Routes**
nested routing is having a Route component inside the opening and closing tag of another Route component as shown below

I embedded a child Route `repo page` in between the home page Route which I also provided with a path and an element.

```
<Route path="/" element={<Home />}>
          <Route path="/repo/:id" element={<RepoPage/>} />
</Route>
```
**Error Boundary**
Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed.

I imported ErrorBoundary from react-error-boundary.
```
import { ErrorBoundary } from 'react-error-boundary'
```
created a fallback component called errorFallback that displays instead of the error breaking the entire app.

```
function ErrorFallback({ error }) {
  return (
    <div role="alert">
      <p>Something went wrong:</p>
      <pre style={{ color: 'red' }}>{error.message}</pre>
    </div>
  )
}
```
I used the ErrorBoundary component provided by react-error-boundary to wrap the components that i want to handle errors for. This component will catch any errors that occur within the wrapped components and display a fallback UI to the user instead of crashing the whole app.

```
  <ErrorBoundary FallbackComponent={ErrorFallback}> 
      <Repos  /> 
  </ErrorBoundary>
```

## Additional feature
finally i was able to add a search feature where people can find other github users and display their profile and repos as well.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ooqwdebp58174bgk5x70.png)

I achieved this by following almost the same process in the API fetch implementation section. which includes

1. creating a state to store the user profile data and user repos

2. creating 2 asynchronous function that `handleSearchuser` and `handleSearchRepo` that make the API calls to fetch the Users profile data and repo.

```
  // NEW FEATURE: Searching for Other GitHub Users
  const handleSearch = async (e) => {
    e.preventDefault();
    if (search === "") return;
    await handleSearchUser();
    await handleSearchRepo();
  };

  const handleSearchUser = async () => {
    const response = await fetch(`${url}/users/${search}`, {
      headers: {
        Authorization: `token ${token}`,
      },
    });
    const data = await response.json();
    setUser(data);
    setLoading(false);
  };

  const handleSearchRepo = async () => {
    const response = await fetch(`${url}/users/${search}/repos`, {
      headers: {
        Authorization: `token ${token}`,
      },
    });

    const data = await response.json();
    setRepos(data);
    setLoading(false);
    console.log(data);
  };
```

3. created a handleSearch that receives and stores the response from `handleSearchuser` and `handleSearchRepo` functions

```
 const handleSearch = async (e) => {
    e.preventDefault();
    if (search === "") return;
    await handleSearchUser();
    await handleSearchRepo();
  };
```

Finally i  created a form that takes in an input and a button, an onchange event is given on the input so as to track the input and an onSubmit event on the form to trigger the `handleSearch` function.
```
<form onSubmit={handleSearch}>
 <input className="search-input" type="text" value={search} onChange={(e)=>setSearch(e.target.value)} placeholder="Search For a Github User" />
 <button className="search-btn"><p>Search</p>  <RightArrow className="btn-arrow" /></button>
</form>
```

## conclusion.
Thank you for taking your time to read this article and maybe learned something.
I loved working on this as i was able to learn and implement various React concepts.  Checkout the source code in my [GitHub repository](https://github.com/ble-syn/myGithub) and the [link](https://my-github-ble-syn.vercel.app/) to the live site.

Thanks Again❤️