Skip to main content

isPending

The isPending function is a TypeScript type guard that checks whether a NetworkState represents an ongoing API request. It provides type-safe access to loading state information by narrowing the type from a general NetworkState<T, E> to a specific PendingState<T, E>.

Overview

isPending is essential for managing loading states in your React components. It ensures that you can safely show loading indicators, progress bars, and disable user interactions during active network requests, providing excellent TypeScript intellisense for loading state management.

Function Signature

function isPending<T, E = unknown>(
state: NetworkState<T, E>
): state is PendingState<T, E>

Type Parameters

ParameterDescription
TThe type of the successful response data
EThe type of the error (defaults to unknown)

Parameters

ParameterTypeDescription
stateNetworkState<T, E>The network state to check

Returns

TypeDescription
state is PendingState<T, E>Type predicate that narrows the state to PendingState<T, E> when true

PendingState Interface

When isPending returns true, the state is narrowed to PendingState<T, E> which includes:

PropertyTypeDescription
state'pending'Always 'pending' for pending states
progressProgress (optional)Upload/download progress information
dataT (optional)Partial data available during request

Progress Interface

The optional progress property provides detailed information about ongoing uploads or downloads:

PropertyTypeDescription
type'upload' | 'download' (optional)Type of operation
loadednumberBytes loaded so far
totalnumber (optional)Total bytes to transfer (if known)

Basic Usage

Simple Loading State

import React from 'react';
import { useGetUsers, isPending, isSuccess, isError } from '@intrig/next';

function UsersList() {
const [usersResponse, fetchUsers] = useGetUsers({
fetchOnMount: true
});

if (isPending(usersResponse)) {
// TypeScript now knows usersResponse has pending state properties
return (
<div className="loading">
<div className="spinner" />
<p>Loading users...</p>
</div>
);
}

if (isError(usersResponse)) {
return (
<div className="error">
<p>Failed to load users</p>
<button onClick={() => fetchUsers()}>Try Again</button>
</div>
);
}

if (isSuccess(usersResponse)) {
return (
<div>
<h2>Users ({usersResponse.data.length})</h2>
<ul>
{usersResponse.data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}

return <div>Click to load users</div>;
}

Loading with Progress Bar

import React from 'react';
import { useUploadFile, isPending } from '@intrig/next';

function FileUploader() {
const [uploadResponse, uploadFile] = useUploadFile({
fetchOnMount: false
});

const handleFileUpload = (file) => {
uploadFile({ body: { file } });
};

if (isPending(uploadResponse)) {
const { progress } = uploadResponse;

if (progress && progress.total) {
const percentage = Math.round((progress.loaded / progress.total) * 100);

return (
<div className="upload-progress">
<p>Uploading... {percentage}%</p>
<progress value={progress.loaded} max={progress.total} />
<p>{progress.loaded} / {progress.total} bytes</p>
</div>
);
}

return (
<div className="uploading">
<div className="spinner" />
<p>Uploading file...</p>
</div>
);
}

return (
<div>
<input
type="file"
onChange={(e) => handleFileUpload(e.target.files[0])}
/>
</div>
);
}

The isPending function provides several TypeScript benefits:

  1. Type Narrowing: After the check, TypeScript knows you have a PendingState<T, E>
  2. Progress Access: Safe access to progress information when available
  3. Compile-time Safety: Prevents accessing pending-specific properties when they might not exist
  4. Partial Data Access: Type-safe access to any partial data available during loading

Best Practices

  1. Provide clear loading feedback: Always show users when operations are in progress
  2. Disable interactions during loading: Prevent duplicate requests by disabling buttons/forms
  3. Use progress indicators: Show progress bars for file uploads or long operations
  4. Handle loading timeouts: Provide escape routes for operations that take too long
  5. Consider skeleton screens: Use skeleton loading for better perceived performance
  6. Implement optimistic updates: Update UI immediately for better user experience

Troubleshooting

Common Issues

Issue: Progress information not available

// ❌ Bad - progress might not exist
if (isPending(response)) {
const percentage = (response.progress.loaded / response.progress.total) * 100;
}

// ✅ Good - always check if progress exists
if (isPending(response)) {
const { progress } = response;
if (progress && progress.total) {
const percentage = (progress.loaded / progress.total) * 100;
}
}

Issue: Loading state doesn't update UI

// ✅ Make sure to include response in dependencies
useEffect(() => {
if (isPending(response)) {
// Handle loading state
}
}, [response]); // Include response in dependencies

Issue: Partial data not accessible

// ❌ Bad - data might not be available
if (isPending(response)) {
return <div>{response.data.name}</div>; // Could be undefined
}

// ✅ Good - check if partial data exists
if (isPending(response)) {
const { data: partialData } = response;
return partialData ? (
<div>Loading: {partialData.name}</div>
) : (
<div>Loading...</div>
);
}