index.js
Overview
This file implements a React component that demonstrates infinite scroll pagination by fetching GitHub issues from a specified repository using the useSWRInfinite hook from the SWR data fetching library.
The core functionality centers around:
Dynamically fetching paged data (GitHub issues) from the GitHub API.
Managing pagination state (loading more pages, refreshing, clearing).
Providing a user interface for inputting the repository name, loading issues, and interacting with the data set.
Handling various loading states and edge cases such as empty results and end-of-data.
This example effectively illustrates how to use useSWRInfinite for paginated API calls in a React component with seamless cache management, revalidation, and UI updates.
Detailed Explanation
Constants
const PAGE_SIZE = 6
Defines the number of GitHub issues fetched per page.
Default Exported Function Component: App
export default function App() { ... }
This React function component encapsulates the entire example application.
State Variables
const [repo, setRepo] = useState('reactjs/react-a11y')
const [val, setVal] = useState(repo)
repo(string): The currently selected GitHub repository in the format"owner/repo". Defaults to"reactjs/react-a11y".val(string): Controlled input state for the repository input field, initialized with the default repo.
Infinite Data Fetching Hook: useSWRInfinite
const { data, error, mutate, size, setSize, isValidating } = useSWRInfinite(
(index) =>
`https://api.github.com/repos/${repo}/issues?per_page=${PAGE_SIZE}&page=${index + 1}`,
fetch
)
Purpose: Fetches GitHub issues page-by-page for the selected repository with a page size of 6.
Parameters:
A page key function that returns the URL to fetch for each page index.
A
fetchfunction imported from../libs/fetchthat handles the HTTP request.
Returns: An object with:
data: An array where each element is an array of issues from each page.error: Any error encountered during fetching.mutate: Function to manually trigger revalidation/refetching.size: Current number of pages loaded.setSize: Function to update the number of pages to load.isValidating: Boolean indicating if data is being fetched/refreshed.
Data Processing and Loading State Flags
const issues = data ? [].concat(...data) : []
const isLoadingInitialData = !data && !error
const isLoadingMore =
isLoadingInitialData ||
(size > 0 && data && typeof data[size - 1] === 'undefined')
const isEmpty = data?.[0]?.length === 0
const isReachingEnd =
isEmpty || (data && data[data.length - 1]?.length < PAGE_SIZE)
const isRefreshing = isValidating && data && data.length === size
issues(array): Flattens the array of pages into a single list of issues.isLoadingInitialData(bool): True if the first fetch is in progress and no data or error exists yet.isLoadingMore(bool): True if the component is loading additional pages.isEmpty(bool): True if the first page of data is empty (no issues).isReachingEnd(bool): True if there are no more issues to load (last page fetched has less thanPAGE_SIZEitems or no data).isRefreshing(bool): True if a revalidation (refresh) is ongoing on all loaded pages.
JSX Structure and User Interface
<div style={{ fontFamily: 'sans-serif' }}>
{/* Repo input */}
<input
value={val}
onChange={(e) => setVal(e.target.value)}
placeholder="reactjs/react-a11y"
/>
{/* Load issues button */}
<button
onClick={() => {
setRepo(val)
setSize(1)
}}
>
load issues
</button>
{/* Status and control buttons */}
<p>
showing {size} page(s) of {isLoadingMore ? '...' : issues.length} issue(s){' '}
<button
disabled={isLoadingMore || isReachingEnd}
onClick={() => setSize(size + 1)}
>
{isLoadingMore
? 'loading...'
: isReachingEnd
? 'no more issues'
: 'load more'}
</button>
<button disabled={isRefreshing} onClick={() => mutate()}>
{isRefreshing ? 'refreshing...' : 'refresh'}
</button>
<button disabled={!size} onClick={() => setSize(0)}>
clear
</button>
</p>
{/* Empty state message */}
{isEmpty ? <p>Yay, no issues found.</p> : null}
{/* List of issues */}
{issues.map((issue) => (
<p key={issue.id} style={{ margin: '6px 0' }}>
- {issue.title}
</p>
))}
</div>
Input field: Allows the user to type a GitHub repo name.
Load issues button: Sets the current repo to the input value and resets page size to 1 to load the first page.
Status text: Shows how many pages and total issues are displayed.
Load more button: Loads next page by incrementing
size. Disabled if loading or no more data.Refresh button: Revalidates all pages, fetching fresh data.
Clear button: Clears all loaded pages by setting size to 0.
Conditional empty state message: Indicates no issues found.
Issue list: Maps over all loaded issues displaying their titles.
Important Implementation Details
Paginated fetching logic: Uses page index to construct API URLs, fetching issues page-by-page.
Concatenation of pages: SWR returns data as an array of pages, so the code flattens it for display.
Loading and boundary states: Various booleans derived to manage UI states and button enable/disable logic.
User input controlled via React state: Decouples input field value (
val) from the active repo (repo) used in fetch keys.Resetting pagination on repo change: When repository changes, resets page size to 1 to fetch the first page of new issues.
SWR's mutate function: Used for refreshing data across all pages.
Edge case handling: Detects empty results and end of data to disable "Load more" button and show messages.
Styling: Minimal inline styles for readability.
Interaction with Other Parts of the System
fetchutility: Imported from../libs/fetchto standardize API requests. Likely wrapsfetch()with JSON parsing and error handling.SWR library: This file depends on SWR's
useSWRInfinitefor caching, revalidation, and data fetching abstraction.GitHub API: Fetches data directly from GitHub's REST API for issues.
React: Uses React state and event handling for UI and interaction management.
This component could be part of a larger app demonstrating SWR usage patterns or serve as a standalone example of infinite scroll pagination with React.
Usage Example
Simply importing and rendering this component will show an input field for a GitHub repository, fetch issues in pages of 6, and allow the user to load more issues, refresh, or clear the list.
import React from 'react'
import App from './index'
function Main() {
return <App />
}
Visual Diagram: Component Workflow Flowchart
flowchart TD
UIInput[User inputs repo name in input field]
SetRepo[Set repo state and reset page size to 1]
FetchPage1[useSWRInfinite fetches page 1 issues]
DisplayIssues[Display issues (flattened from all pages)]
LoadMoreBtn[User clicks "Load More" button]
IncrementSize[Increment page size by 1]
FetchNextPage[Fetch next page issues]
RefreshBtn[User clicks "Refresh" button]
MutateCall[Call mutate() to revalidate all pages]
ClearBtn[User clicks "Clear" button]
ResetSize[Reset page size to 0]
EmptyState[Show "No issues found" message if empty]
UIInput --> SetRepo
SetRepo --> FetchPage1
FetchPage1 --> DisplayIssues
DisplayIssues --> LoadMoreBtn
LoadMoreBtn --> IncrementSize
IncrementSize --> FetchNextPage
FetchNextPage --> DisplayIssues
DisplayIssues --> RefreshBtn
RefreshBtn --> MutateCall
MutateCall --> FetchPage1
DisplayIssues --> ClearBtn
ClearBtn --> ResetSize
ResetSize --> EmptyState
Summary
Implements an infinite scroll example fetching GitHub issues paginated by 6 per page.
Uses
useSWRInfinitefor efficient caching and incremental loading.Supports user input for repo selection with resettable pagination.
Manages UI states for loading, refreshing, empty results, and end of data.
Provides buttons to load more pages, refresh data, and clear the list.
Interacts with GitHub API, SWR library, and a custom fetch utility.
Serves as a practical demonstration of SWR infinite loading in React.
This file is a focused and clear example of handling paginated API data in React apps using SWR's infinite loading capabilities.