Async Cell

Generic component for displaying data by ID with automatic batched fetching

Live Example

Basic AsyncCell

Display project names by ID with automatic batching

Project 759: -
Project 760: -
Project 761: -

ℹ️ These 3 components result in ONE API call with id__in=759,760,761

Custom Rendering

Use a custom render function to display complex data

Unknown project

Specialized Components

Pre-built components for common use cases

Project: #759
Organization: #1

With Loading State

Show custom loading and fallback states

Loading...

Overview

The AsyncCell component is a generic utility for displaying data fetched asynchronously by ID. It uses intelligent batching to combine multiple requests into a single API call, making it perfect for table cells and other scenarios where you need to display related data by ID.

Features

  • Automatic Batching: All AsyncCell components rendered within 50ms have their requests batched into a single API call using id__in
  • Smart Caching: If the item is already in the collection, it returns immediately from cache without making an API call
  • 50ms Debounce: Waits 50ms to collect all IDs before making the API call
  • Type-Safe: Fully typed with TypeScript generics
  • Flexible Rendering: Custom render function or automatic rendering of the name property

Usage

Basic Usage (Auto-renders name)

import { useSGERP } from 'sgerp-frontend-lib';
import { AsyncCell } from '@/components/sgerp/async-cell';

function MyComponent() {
  const api = useSGERP();

  return (
    <AsyncCell
      collection={api?.collections.project}
      id={123}
    />
  );
}

Custom Rendering

<AsyncCell
  collection={api?.collections.vehicle}
  id={vehicleId}
  render={(vehicle) => (
    <div>
      <span>{vehicle.service_number}</span>
      <span className="text-muted-foreground ml-2">({vehicle.color})</span>
    </div>
  )}
  fallback={<span className="text-muted-foreground">Unknown vehicle</span>}
/>

In Table Columns

const columns: ColumnDef<Booking>[] = [
  {
    key: 'project_id',
    label: 'Project',
    render: (_, booking) => (
      <AsyncCell
        collection={api?.collections.project}
        id={booking.project_id}
        render={(project) => project.name}
      />
    ),
  },
];

With Loading State

<AsyncCell
  collection={api?.collections.passenger}
  id={passengerId}
  render={(passenger) => `${passenger.first_name} ${passenger.last_name}`}
  loadingFallback={<span className="text-muted-foreground">Loading...</span>}
  fallback={<span className="text-muted-foreground">Unknown</span>}
/>

Props

PropTypeRequiredDescription
collectionCollection<T>YesThe collection to fetch from
idnumber | stringYesThe ID of the item to fetch
render(item: T) => ReactNodeNoCustom render function. Defaults to rendering the name property
fallbackReactNodeNoContent to show when item is not found. Default: '-'
loadingFallbackReactNodeNoContent to show while loading. If not provided, shows fallback

How Batching Works

When multiple AsyncCell components are rendered:

  1. Each component requests its item by ID via collection.getAsync(id)
  2. Requests are queued for 50ms (debounced)
  3. After 50ms, all queued IDs are combined into a single API call: ?model=project&id__in=1,2,3,4,5
  4. Results are distributed to all waiting components
  5. Subsequent renders for the same ID use cached data (no API call)

Example:

// These 5 components will result in ONE API call:
// GET /api/v2/microservices/get?model=project&id__in=1,2,3,4,5

<AsyncCell collection={api?.collections.project} id={1} />
<AsyncCell collection={api?.collections.project} id={2} />
<AsyncCell collection={api?.collections.project} id={3} />
<AsyncCell collection={api?.collections.project} id={4} />
<AsyncCell collection={api?.collections.project} id={5} />

Specialized Components

The library includes pre-built AsyncCell wrappers for common use cases:

ProjectName

import { ProjectName } from '@/components/sgerp/project-name';

<ProjectName projectId={123} />

OrganizationName

import { OrganizationName } from '@/components/sgerp/organization-name';

<OrganizationName organizationId={456} />

Performance Tips

  1. Use in tables: Perfect for displaying related data in table columns
  2. Avoid unnecessary re-renders: Memoize your render function if it's complex
  3. Pre-fetch data: If you know you'll need certain items, consider pre-fetching them into the collection
  4. Cache benefits: Once an item is fetched, all subsequent renders use the cache