3. Using APIs
This step takes our static components and populates them with data from the GitHub GraphQL API – loading states and all. We’ll be displaying Carbon repository information in a data table.
- Fork, clone and branch
- Install dependencies
- Fetch data
- Populate data table
- Add loading
- Add pagination
- Submit pull request
Preview
The GitHub REST API is very well documented, we’ll use it to fetch Carbon-related data for this Carbon tutorial.
To do so, we’ll be using Octokit Core, a client that makes it easy to interact with GitHub’s APIs.
A preview of what you will build (see repositories page):
Fork, clone and branch
This tutorial has an accompanying GitHub repository called carbon-tutorial-nextjs that we’ll use as a starting point for each step. If you haven’t forked and cloned that repository yet, and haven’t added the upstream remote, go ahead and do so by following the step 1 instructions.
Branch
With your repository all set up, let’s check out the branch for this tutorial step’s starting point.
git fetch upstreamgit checkout -b v11-next-step-3 upstream/v11-next-step-3
Build and start app
Install the app’s dependencies and build the app:
yarn && yarn build
Then, start the app:
yarn start
You should see something similar to where the previous step left off. Stop your app with
CTRL-C
Install dependencies
We’ll need to install
@octokit/core
CTRL-C
yarn add @octokit/core@4.2.0
Then, start the app again. If your app’s currently running, you’ll need to restart it.
yarn dev
Fetch data
Imports
We’ll be using React Hooks to call a function to fetch our data when the component renders.
import React, { useEffect } from 'react';
Add the following import below the react import in
RepoPage
import { Octokit } from '@octokit/core';
Initializing Octokit client
Directly below all your imports, initialize an octokit client which we’ll use to query our
RepoTable
const octokitClient = new Octokit({});
API Request
Next, we’ll assemble our GitHub API request to fetch a list of repositories that belong to the
carbon-design-system
useEffect
Let’s declare a
useEffect
function RepoPage() {useEffect(() => {async function getCarbonRepos() {const res = await octokitClient.request('GET /orgs/{org}/repos', {org: 'carbon-design-system',per_page: 75,sort: 'updated',direction: 'desc',});
At this point, if you navigate to the Repositories page
/repos
Helpers
Our last column in the data table will be a comma-separated list of repository and home page links, so let’s create a component called
LinkList
Import
Link
/app/repos/page.js
import { Link, Grid, Column } from '@carbon/react';
Then use
Link
url
homepageUrl
const LinkList = ({ url, homepageUrl }) => (<ul style={{ display: 'flex' }}><li><Link href={url}>GitHub</Link></li>{homepageUrl && (<li><span> | </span><Link href={homepageUrl}>Homepage</Link>
As a final helper, let’s create a function that transforms row data to our expected header keys. Notice how we’re using our new
LinkList
links
const getRowItems = (rows) =>rows.map((row) => ({...row,key: row.id,stars: row.stargazers_count,issueCount: row.open_issues_count,createdAt: new Date(row.created_at).toLocaleDateString(),updatedAt: new Date(row.updated_at).toLocaleDateString(),links: <LinkList url={row.html_url} homepageUrl={row.homepage} />,
Populate data table
Now that we have our data, let’s dispose of our dummy
rows
First, towards the top of
RepoPage
rows
Next, let’s add a couple variables that will help us store useful information when fetching the data and keep track of the loading state.
We’ll be using React Hooks again to manage our state.
Import React’s useState by modifying the
React
import React, { useEffect, useState } from 'react';
Then, inside the
RepoPage
function RepoPage() {const [loading, setLoading] = useState(true);const [error, setError] = useState();const [rows, setRows] = useState([]);
Now, instead of using
console.log
getRowItems
rows
console.log(res.data);
if (res.status === 200)
setRows(getRowItems(res.data));
Let’s also replace our error log line inside the
else
setError('Error obtaining repository data');
To complete our
getCarbonRepos
else
if (res.status === 200) {setRows(getRowItems(res.data));} else {setError('Error obtaining repository data');}setLoading(false);
Finally, let’s modify our component’s
return()
if (loading) {return 'Loading...';}if (error) {return `Error! ${error}`;}// If we're here, we've got our data!
Render repository descriptions
The data table component and its pieces use a common React pattern called render props. This is a really powerful way for libraries to give developers control of rendering and manipulating their data.
Revisiting
RepoTable.js
render
One common hurdle with the data table is how to access data that might not correspond with a table column but is needed to compute the value of a cell that does. The data table component processes and controls only the row properties which corresponds to headers (columns). Because of this, the
rows
rows
We need to modify one aspect of the data table because if you expand a row, it says
Row description
To do so, in
RepoTable.js
RepoTable
return()
const getRowDescription = (rowId) => {const row = rows.find(({ id }) => id === rowId);return row ? row.description : '';};
Finally, in
RepoTable.js
<p>Row description</p>
<p>{getRowDescription(row.id)}</p>
Add loading
At this point, the first time that you visit the repositories page, we’re querying the GitHub API and rendering the response through the
DataTable
Loading...
To do so, back to
RepoPage
DataTableSkeleton
@carbon/react
import { Link, DataTableSkeleton, Grid, Column } from '@carbon/react';
Then replace the
if (loading) return 'Loading...';
if (loading) {return (<Grid className="repo-page"><Column lg={16} md={8} sm={4} className="repo-page__r1"><DataTableSkeletoncolumnCount={headers.length + 1}rowCount={10}headers={headers}/>
We need to tell the loading skeleton how many rows to render, so let’s use 10 skeleton rows to prepare for the next enhancement…
Add pagination
Pagination! Instead of rendering every repository, let’s add pagination to the data table to only render 10 at a time. Depending on your specific requirements, you may need to fetch new data each time that you interact with the pagination component, but for simplicity, we’re going to make one request to fetch all data, and then paginate the in-memory row data.
Initialize the new state variables that we’ll use for pagination as the first lines inside the
RepoPage
function RepoPage() {const [firstRowIndex, setFirstRowIndex] = useState(0);const [currentPageSize, setCurrentPageSize] = useState(10);...
This initializes the total number of rows and the index of the first row to
0
10
Then we need to update our
RepoTable
rows
<RepoTable headers={headers} rows={rows} />
<RepoTableheaders={headers}rows={rows.slice(firstRowIndex, firstRowIndex + currentPageSize)}/>
Finally, let’s add the
Pagination
Import
Pagination
@carbon/react
import {Link,DataTableSkeleton,Pagination,Grid,Column,} from '@carbon/react';
Immediately after the
RepoTable
/>
Pagination
<PaginationtotalItems={rows.length}backwardText="Previous page"forwardText="Next page"pageSize={currentPageSize}pageSizes={[5, 10, 15, 25]}itemsPerPageText="Items per page"onChange={({ page, pageSize }) => {if (pageSize !== currentPageSize) {
That does it! Your data table should fetch GitHub data on first render. You can expand each row to see the repository’s description. You can modify the pagination items per page and cycle through pages or jump to a specific page of repositories.
Submit pull request
We’re going to submit a pull request to verify completion of this tutorial step.
Continuous integration (CI) check
Run the CI check to make sure we’re all set to submit a pull request.
yarn ci-check
Git commit and push
Before we can create a pull request, format your code, then stage and commit all of your changes:
yarn formatgit add --all && git commit -m "feat(tutorial): complete step 3"
Then, push to your repository:
git push origin v11-next-step-3
Pull request (PR)
Finally, visit carbon-tutorial-nextjs to “Compare & pull request”. In doing so, make sure that you are comparing to
v11-next-step-3
base: v11-next-step-3