Building a React App that gets all repositories for a single user with GitHub API

Building a React App that gets all repositories for a single user with GitHub API

Table of contents

No heading

No headings in the article.

I built an app that gives me an overview of all my GitHub repositories and I also added a feature that helps me find Github Repo for various users. It uses GitHub API. This app implements an API fetch of my GitHub portfolio, and also shows a page with a list of all the repositories on my GitHub. It also shows the details of a single repo clicked, and an error boundary page, that test the error boundary.

Here is a live link to the project: https://my-repository-website.vercel.app/

GitHub link for the project: https://github.com/debbyuzuegbu/MyRepositoryWebsite.

List of Dependencies used:

  • react

  • react-router-dom

  • react-error-boundary

  • react-icons

  • styled-components

  • axios.

Here are a few insights into building an app that implements GitHub API:

Kindly note that I discussed getting my repositories on a single page in this article.

Step 1: UI Design

Consider the UI design you would want to implement into your project e.g SCSS, tailwind CSS, Bootstrap, Chakra UI etc For me I used the styled component. I like styled-component because it CSS-in-JS. It encapsulates CSS inside a component and so I do not have to worry about classNames clashing.

Step 2: Features to be Implemented.

I think understanding what features you would love to implement in your project is quite great.

For me there were a few tips I took into consideration:

  • I have a <button> element, with a text View My Repository this leads you to the overview of all my repositories.

  • Being able to navigate across various pages. I knew I needed to import react-router.

  • Each repository can be clicked to give more details about the repository e.g how many forks the repository has etc.

  • We have a button Search now that will be used to search for any GitHub username and gives an overview of all repositories for that username.

Step 3: Get React Working

I went ahead to install Node.js in my system, after which I created a directory with the name Project(just a folder to store all my react projects) then, I opened the terminal and navigated into that directory. There, I ran the following command

npx create-react-app myapp
npm start

Do note that 'myapp' can be any name of your choice.

Step 4: Create a component folder to store all your components.

Note when creating a component file, don’t be afraid to split components into smaller components.

When naming components, give names that are linked to what they do or where they are located e.g Footer, Searchbar, Navbar etc. Also when naming components, use Pascal's case.

Step 5: Implementing Github API

First, understanding how to consume Restful API is key.

A brief overview of what Rest APIs are: a REST API is an API that maps a resource to an endpoint. Resource, in this case, means any piece of data necessary for an application to function e.g let's use our GitHub Rest API, a list of users resource(s) could be mapped to a /user endpoint.

To learn more about Rest APIs here is a link: https://pusher.com/tutorials/consume-restful-api-react/

I got a GitHub API resource to list repositories for a user using the get request method to the user's endpoint. I went ahead to fetch data(I think this is the easiest method to make an API request) from our GitHub API endpoint. For making API calls, I like using Axios.

You can install Axios using npm or yarn in your project directory.

npm install axios

Once installed, import it into your JavaScript file like the code below:

import axios from "axios";

Then, initialize two state variables that would be used to store the response from calling it in react and also monitor the loading state.

import React, { useState } from "react";
import axios from "axios";
import { Link } from "react-router-dom";

function MyRepo() {
  const [repos, setRepos] = useState([]);
  const [loading, setLoading] = useState(false);
}

Then I queried the REST API endpoint when the component(MyRepo) is mounted and for that, I used the useEffect hook.

import React, { useState, useEffect } from "react";

function MyRepo() {
  const [repos, setRepos] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(async () => {
    try {
      setLoading(true);
      const result = await axios(
        "https://api.github.com/users/debbyuzuegbu/repos"
      );
      setRepos(result.data);
      setLoading(false);
    } catch (err) {
      console.log(err);
    }
    ;
  }, []);

In a bid to make my code more readable, I created an arrow function to fetch a list of all my repositories.

function MyRepo() {
  const [repos, setRepos] = useState([]);
  const [loading, setLoading] = useState(false);

  const fetchRepos = async () => {
    try {
      setLoading(true);
      const result = await axios(
        "https://api.github.com/users/debbyuzuegbu/repos"
      );
      setRepos(result.data);
      setLoading(false);
    } catch (err) {
      console.log(err);
    }
  };
  useEffect(() => {
    fetchRepos();
  }, []);

Finally, we can render the data fetched from the /users endpoint. First, I checked if there are any errors before rendering and then looped over the list of repositories in the state. (hint I used console.log).

Step 6: Implement Pagination

So I saw documentation that helped me out with this (Link to documentation)

I decided on how many records are to be displayed on a single page and the number of pages. I also thought about keeping track of the page number, the user is on. These two states were what I created.

import React, { useState, useContext } from "react";
import axios from "axios";
import { MyContextApi } from "../App";
import { useEffect } from "react";
import { Link } from "react-router-dom";

function MyRepo() {
  const [repos, setRepos] = useContext(MyContextApi);
  const [loading, setLoading] = useState(false);
  const [page, setPage] = useState(1);
  const [numberOfPost] = useState(5);
  const fetchRepos = async () => {
    try {
      setLoading(true);
      const result = await axios(
        "https://api.github.com/users/debbyuzuegbu/repos"
      );
      setRepos(result.data);
      setLoading(false);
    } catch (err) {
      console.log(err);
    }
  };
  useEffect(() => {
    fetchRepos();
  }, []);

I decided on the indices of the first and last record on the current page

import React, { useState, useContext } from "react";
import axios from "axios";
import { MyContextApi } from "../App";
import { useEffect } from "react";
import { Link } from "react-router-dom";

function MyRepo() {
  const [repos, setRepos] = useContext(MyContextApi);
  const [loading, setLoading] = useState(false);
  const [page, setPage] = useState(1);
  const [numberOfPost] = useState(5);
  const fetchRepos = async () => {
    try {
      setLoading(true);
      const result = await axios(
        "https://api.github.com/users/debbyuzuegbu/repos"
      );
      setRepos(result.data);
      setLoading(false);
    } catch (err) {
      console.log(err);
    }
  };
  useEffect(() => {
    fetchRepos();
  }, []);
  const pages = 6;
  const lastIndexPage = numberOfPost * page;
  const firstIndexPage = lastIndexPage - numberOfPost;

Inside the component, I created an array that holds all the page numbers from 1 to nPages. used <button>element.

import React, { useState, useContext } from "react";
import axios from "axios";
import { MyContextApi } from "../App";
import { useEffect } from "react";
import { Link } from "react-router-dom";

function MyRepo() {
  const [repos, setRepos] = useContext(MyContextApi);
  const [loading, setLoading] = useState(false);
  const [page, setPage] = useState(1);
  const [numberOfPost] = useState(5);
  const fetchRepos = async () => {
    try {
      setLoading(true);
      const result = await axios(
        "https://api.github.com/users/debbyuzuegbu/repos"
      );
      setRepos(result.data);
      setLoading(false);
    } catch (err) {
      console.log(err);
    }
  };
  useEffect(() => {
    fetchRepos();
  }, []);
  const pages = 6;
  const lastIndexPage = numberOfPost * page;
  const firstIndexPage = lastIndexPage - numberOfPost;
  const currentPost = repos.slice(firstIndexPage, lastIndexPage);
  console.log(repos)
  return (
    <div>
            <MyRepoCardContent>
              {loading ? (
                <h3>Loading...</h3>
              ) : (
                repos &&
                currentPost.map((rep) => (
                  <MyRepoCard key={rep.id}>
                    <MyRepoCardTitle>{rep.name}</MyRepoCardTitle>
                    <MyRepoCardText>{rep.visibility}</MyRepoCardText>
                    <MyRepoCardText>{rep.forks}</MyRepoCardText>
                    <MyRepoCardText>{rep.created_at}</MyRepoCardText>
                    <MyRepoCardText style={{fontSize: "8px"}}>{rep.contributors_url}</MyRepoCardText>
                    <Link to={`/repo/${rep.id}`}>See More</Link>
                  </MyRepoCard>
                ))
              )}
          </MyRepoCardContent>
       <div style={{alignItems: "center", display: "flex", justifyContent: "center", padding:"20px"}}>
       <Button
          disabled={page <= 1 ? true : null}
          onClick={() => setPage((s) => Number(s) - 1)}
        >
         Prev
      </Button>
      {Array.from({ length: 6 }, (v, i) => i + 1).map((n) => (
          <Button key={n} onClick={(e) => setPage(e.target.value)} value={n}>
            {n}
         </Button>
        ))}
       <Button
          disabled={page >= pages ? true : null}
          onClick={() => setPage((s) => Number(s) + 1)}
        >
        Next
       </Button>
     </div>
    </div>
  );
}

export default MyRepo;

I added context to manage my state globally and get my data shared across all components.

Here is the code to fetch and display all repositories for a single user.

import React, { useState, useContext } from "react";
import axios from "axios";
import { MyContextApi } from "../App";
import { useEffect } from "react";
import { Link } from "react-router-dom";

import {
  MyRepoCardContent,
  MyRepoCard,
  MyRepoCardTitle,
  MyRepoCardText,
  Button,
} from "./MyRepo.styles";

function MyRepo() {
  const [repos, setRepos] = useContext(MyContextApi);
  const [loading, setLoading] = useState(false);
  const [page, setPage] = useState(1);
  const [numberOfPost] = useState(5);
  const fetchRepos = async () => {
    try {
      setLoading(true);
      const result = await axios(
        "https://api.github.com/users/debbyuzuegbu/repos"
      );
      setRepos(result.data);
      setLoading(false);
    } catch (err) {
      console.log(err);
    }
  };
  useEffect(() => {
    fetchRepos();
  }, []);
  const pages = 6;
  const lastIndexPage = numberOfPost * page;
  const firstIndexPage = lastIndexPage - numberOfPost;
  const currentPost = repos.slice(firstIndexPage, lastIndexPage);
  console.log(repos)
  return (
    <div>
            <MyRepoCardContent>
              {loading ? (
                <h3>Loading...</h3>
              ) : (
                repos &&
                currentPost.map((rep) => (
                  <MyRepoCard key={rep.id}>
                    <MyRepoCardTitle>{rep.name}</MyRepoCardTitle>
                    <MyRepoCardText>{rep.visibility}</MyRepoCardText>
                    <MyRepoCardText>{rep.forks}</MyRepoCardText>
                    <MyRepoCardText>{rep.created_at}</MyRepoCardText>
                    <MyRepoCardText style={{fontSize: "8px"}}>{rep.contributors_url}</MyRepoCardText>
                    <Link to={`/repo/${rep.id}`}>See More</Link>
                  </MyRepoCard>
                ))
              )}
          </MyRepoCardContent>
       <div style={{alignItems: "center", display: "flex", justifyContent: "center", padding:"20px"}}>
       <Button
          disabled={page <= 1 ? true : null}
          onClick={() => setPage((s) => Number(s) - 1)}
        >
         Prev
      </Button>
      {Array.from({ length: 6 }, (v, i) => i + 1).map((n) => (
          <Button key={n} onClick={(e) => setPage(e.target.value)} value={n}>
            {n}
         </Button>
        ))}
       <Button
          disabled={page >= pages ? true : null}
          onClick={() => setPage((s) => Number(s) + 1)}
        >
        Next
       </Button>
     </div>
    </div>
  );
}

export default MyRepo;

Conclusion

To Build a react app that gets all repositories for a single user with GitHub API, you would need to follow these steps:

  1. UI Design

  2. Understanding the features you would want to implement.

  3. Get react started on your text editor.

  4. Import all dependencies to be used.

  5. Create a component folder to store all your components.

  6. Implement GitHub API.

  7. Implement Pagination.