DEV Community

Joanna Egbuna
Joanna Egbuna

Posted on

Creating a GitHub App with React

As an Examination project, I worked on developing an application using React; a JavaScript Library for building user interfaces. My application fetches my GitHub repositories from the GitHub site and displays them on my App. And I'd like to give you a detailed step-by-step guide and explanation of my project.

Below is a list of features and functionalities that were implemented on my app:

  • An API fetch of my GitHub portfolio,
  • React Router and Nested routes,
  • Error boundary and a page to test it,
  • A 404 page,
  • A page listing all of my repositories on GitHub,
  • Another page showing data for a single repo clicked from the list of repositories,
  • Pagination for my repository list,
  • SEO implementation,
  • Lazy loading state.

Getting started

Some perquisites for getting started on an application like this include

  • A code editor; I'd recommend VS code because it is easy to use, and

  • A React application already working on your computer. You can check this out for more on creating a React app.

An API fetch of my GitHub Portfolio

APIs are used in Web applications to connect user-facing front end with all-important back-end functionality and data. In this case, I used an API to fetch data from my GitHub portfolio and display it on my page.

This was used to call or fetch the data that was displayed on the page.

import { useEffect } from React

useEffect(() => {
    const link = "https://api.github.com/users/{yourGitHubUsername}/repos";
    const fetchUsers = async () => {
      const res = await fetch(link);
      const data = await res.json();
      console.log(data);
    };
    fetchUsers();
  }, []);
Enter fullscreen mode Exit fullscreen mode

Setting the Router component

The React Router is used to define multiple routes in the application. When a user types a specific URL into the browser, and if this URL path matches any 'route' inside the router file, the user will be redirected to that particular route.

In other words, it is used for navigation between pages or components in a React app. It comes in handy when creating a great website.

To install react-router, you can run the code below in your terminal

npm install react-router-dom
Enter fullscreen mode Exit fullscreen mode

Nested Routes

Just like the name implies, nested routes are routes nested or embedded in another route. They enable you to render multiple components on the same page.

My app consisted of my landing or home page, repo page, repo details page which is nested inside the repo page, error page, and 404 page.

import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";

<BrowserRouter>
     <Routes>
       <Route path="/" element= {<Navigate to="/home" replace={true}/>} />
       <Route path="/home" element= {<Home />} />
       <Route path="/errorpage" element={<ErrorPage />} />
       <Route path="/repos" element={<Repo />} />
       <Route path="/repos/:repoId" element={<Details />} />
       <Route path="*" element={<NotFound />} />
     </Routes> 
 </BrowserRouter>
Enter fullscreen mode Exit fullscreen mode

Error Boundary page

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 crashed site. Error boundaries catch errors during rendering, in lifecycle methods, and constructors of the whole tree below them.

import { Component } from "react";
import { Link } from "react-router-dom";

export class ErrorBoundary extends Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false };
    }

    static getDerivedStateFromError(error) {
        return { hasError: true };
    }

    componentDidCatch(error, errorInfo) {
        console.log("Logging", error, errorInfo);
    }

    render() {
        if (this.state.hasError) {
            return <>
            <h1>OOPS!! <p>SOMETHING WENT WRONG</p></h1>
            <Link to="/">Go home</Link>
            </>;
        }

        return this.props.children;
    }
}

Enter fullscreen mode Exit fullscreen mode

After creating the error boundary you wrap it around your entire app or any part of your app that is bound to have errors.

<ErrorBoundary >
<App />
</ErrorBoundary>
Enter fullscreen mode Exit fullscreen mode

404 page

A 404 page is a page that tells your users that the page they are looking for doesn't exist on that website. This page is displayed to tell the user that they have entered an invalid URL or clicked an invalid or broken link.

import { Link } from "react-router-dom"

const NotFound = () => {
  return (

    <div className="not-found">
      <h2>Sorry</h2>
      <p>That page cannot be found</p>
      <Link to="/">Back to the homepage...</Link>

    </div>
  );
}

export default NotFound;

// the route path for this page is
<Route path="*" element={<NotFound />} />
Enter fullscreen mode Exit fullscreen mode

A page displaying a list of my Repos

A page is needed to display your list of repositories, it could be your landing ie. home page, or different from your homepage. In my case, My repo page was different from my homepage.

I worked on making my repo page simple like the rest of my website and it displayed only my list of repositories. I decided to display only a few pieces of information about the repositories and a link for further information on a particular repository.

import { Link } from react-router-dom

function RepoDetails(props) {
const reposMapped = currentRepos.map((item, index) => (
    <RepoDetails
      key={item.id}
      title={item.name}
      owner={item.owner.login}
      id={item.id}
      description={item.description}
      Avatar={item.owner.avatar_url}
    />
  ));
  return (
    <div className="repo-container">
      <div>
          <img src={props.Avatar} alt="jj" />
        <h1 className="repo-title">{props.title}</h1>
        <div className="owner">{props.owner}</div>
      </div>
      <p>{props.description ? props.description : "No description added"}</p>

      <Link to={`/repos/${props.title}`} className="more">
        CHECK OUT REPO
      </Link>
    </div>
  );
}
export default RepoDetails;
Enter fullscreen mode Exit fullscreen mode

A single page for your repo details

This page is nested inside the repo page and opens when the user clicks on the learn more button on my repo page. When clicked, it displays more information about a particular repo.

import { Link } from "react-router-dom";

export default function Details() {

 return (
  <>
   <div className="all">
     <h1 className="repoName"> {repos.name} </h1>
     <p>LANGUAGE: {repos.language ? repos.language : "No language Used "}</p>
      <p>DEFAULT BRANCH: {repos.default_branch}</p>
      <p className="date">
         CREATED ON {new Date(repos.created_at).toDateString()}
       </p>
       <p className="date">
         LAST UPDATE {new Date(repos.updated_at).toDateString()}
        </p>
       <p>VISIBILITY: {repos.visibility}</p>
      <button>
         <a className="more" href={repos.html_url}>
            VIEW REPO ON GITHUB
         </a>
       </button>
      <button>
         <Link className="more" to="/repos">
            REPOSITORIES
          </Link>
       </button>
      </div>
    </>
  );
}

//route to page
<Route path="/repos/:repoId" element={<Details />} />
Enter fullscreen mode Exit fullscreen mode

Adding Pagination to my repo page

Pagination in React JS is a reusable component that allows data to be displayed on a series of pages. It helps you to display a large number of records and improve the user experience. Simply put, pagination makes use of links and buttons such as previous, next, and page numbers to navigate through the different pages of the website.

I worked basically on the previous, next, and page buttons, but I didn't implement the page number buttons on my page, you can add them to yours if you want to.

import { useEffect, useState } from "react";

function Repos() {
  const [repos, setRepos] = useState([]);
  const [currentPage, setCurrentPage] = useState(1);
  const [perPage] = useState(4);
  const [Loading, setLoading] = useState(true);

  const pagelength = repos.length;
  const indexOfLastRepo = currentPage * perPage;
  const indexOfFirstRepo = indexOfLastRepo - perPage;
  const currentRepos = repos.slice(indexOfFirstRepo, indexOfLastRepo);

  const pageNoArr = [];
  let reposLength = Math.ceil(pagelength / perPage);
  for (let i = 1; i <= reposLength; i++) {
    pageNoArr.push(i);
  }

  const numberOfPage = pageNoArr.map((number) => {
    return (
      <button
        key={number}
        onClick={(e) => setCurrentPage(number)}
        className="page-link"
      >
        {number}
      </button>
    );
  });

  const reposMapped = currentRepos.map((item, index) => (
    <RepoDetails
      key={item.id}
      title={item.name}
      index={index}
      owner={item.owner.login}
      id={item.id}
      description={item.description}
      Avatar={item.owner.avatar_url}
    />
  ));

  return (
    <>
      <Helmet>
        <title>JOANNA REPOSITORIES</title>
        <meta
          name="description"
          content="THIS PAGE DISPLAYS JOANNA'S GITHUB REPOSITORY"
        />
      </Helmet>

      {Loading ? (
        <h1> LOADING...</h1>
      ) : (
        <div className="All">
          <div className="repo-details">{reposMapped}</div>
          <br></br>
          <section className="pagination">
            <button
              className="prev"
              disabled={currentPage <= 1}
              aria-disabled={currentPage <= 1}
              onClick={() => setCurrentPage((prev) => prev - 1)}
            >
              PREV
            </button>
            {/* <div className="page">{numberOfPage}</div> */}
            <button
              className="next"
              disabled={currentPage >= reposLength}
              aria-disabled={currentPage >= 1}
              onClick={() => setCurrentPage((prev) => prev + 1)}
            >
              NEXT
            </button>
          </section>
        </div>
      )}
    </>
  );
}

export default Repos;
Enter fullscreen mode Exit fullscreen mode

SEO Implementation

SEO stands for “search engine optimization.” In simple terms, it means the process of improving your site to increase its visibility when people search for products or services related to your business in any search engine such as Google.

It attracts prospective viewers, users, and customers to your site or business.

To implement SEO, you have to install the react-helmet-async package first.

npm install react-helmet-async

Enter fullscreen mode Exit fullscreen mode

Next, you import the HelmetProvider and use it to wrap your entire app in your root component.

import { HelmetProvider } from "react-helmet-async";

<HelmetProvider>
    <App />
</HelmetProvider>
Enter fullscreen mode Exit fullscreen mode

Lastly, you'd import the helmet component into every page that needs a description and you add a title and description of your choice.

import { Helmet } from 'react-helmet-async';

<Helmet>
   <title>REPOSITORY VIEW</title>
     <meta
       name="description"
       content="THIS PAGE HELPS YOU VIEW JOANNA'S REPOSITORIES ON GITHUB"
    />
 </Helmet>
Enter fullscreen mode Exit fullscreen mode

Lazy loading state

React Lazy Load is an easy-to-use React component that helps you defer loading content in a predictable way. Lazy loading effectively speeds up an application as it defers loading non-critical components. i.e, a component is loaded only when it is needed.

import { lazy, Suspense } from "react";
import { ImSpinner7 } from "react-icons/im";

const Home = lazy(() => import("./pages/Home/Home"));
const ErrorPage = lazy(() => import("./pages/ErrorPage/ErrorPage"));

<Suspense
  fallback={
    <div className="Loading-state">
      <ImSpinner7 className="Loading-spinner" />
    </div>
  }
>
<Routes />
</Suspense>

Enter fullscreen mode Exit fullscreen mode

Conclusion

We learned how to build an application that uses GitHub APIs to fetch your GitHub repositories and display them on a website. We also implemented some necessary and cool features to our app.

Check out my source code here and my live link here.

Don't forget to leave comments and reviews behind for me!!

T for Thanks!!!

Top comments (1)

Collapse
 
osebest profile image
Udazi Ephraim Oseiwe

Great work ma'am