๐ GitHub Contributors Dashboard
This demo shows the top 30 contributors of the facebook/react repository.
It uses the GitHub API to fetch contributor data and displays it in a grid.
Implementation Guide
๐ View Source CodeConverting an API Response to a Matrix with oa2aa
oa2aa (Object Array to Array Array) is a utility that converts an array of objects into the 2D array format that GridSheet expects.
const fields = ['id', 'avatar_url', 'login', 'html_url', 'contributions'];
const response = await request('GET /repos/{owner}/{repo}/contributors', { ... });
setData(oa2aa(response.data as { [s: string]: any }[], fields));Specify the keys you want to extract in fields in the desired order, and each object will be projected into a row in that column order. The order of fields directly determines the column order.
Loading Dynamic Data with buildInitialCellsFromOrigin
After fetching data from an API, pass the matrix to buildInitialCellsFromOrigin to populate the grid starting from A1. Unlike the matrices key in buildInitialCells, the origin is always fixed at A1.
<GridSheet
book={book}
sheetName="contributors"
initialCells={buildInitialCellsFromOrigin({
matrix: data, // the state value from useState
cells: { ... }, // column styles and header definitions go here
})}
/>Although initialCells is recomputed whenever data changes, GridSheet only reads it on first initialization. Toggling rendering with the loading flag is the reliable way to handle this.
Post-processing Rendered Output with renderCallback
renderCallback on PolicyMixinType is a hook that runs after the type-specific render method (renderString, renderNumber, etc.) has executed. It is ideal for adding decorations that are independent of the cell's value type โ such as badges or address overlays.
const AddressOverlayMixin: PolicyMixinType = {
renderCallback(rendered: any, { point }: RenderProps) {
const address = p2a(point);
return (
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
<span style={{ position: 'absolute', top: 2, right: 4, fontSize: '9px', color: '#aaa' }}>
{address}
</span>
{rendered}
</div>
);
},
};rendered is the JSX already returned by renderString or similar. p2a(point) converts the { y, x } coordinate to a human-readable address string like "A1".
Composing Policy Mixins
A single Policy can combine multiple mixins. Later mixins override earlier ones, so place shared behaviors first and specific overrides last.
const book = useSpellbook({
policies: {
avatar: new Policy({
mixins: [
ImagePolicyMixin, // renders the image via renderString
{ renderColHeaderLabel: () => 'Avatar' }, // overrides the column header label
AddressOverlayMixin, // adds the address badge via renderCallback
],
}),
contributions: new Policy({
mixins: [
ThousandSeparatorPolicyMixin, // formats numbers with thousand separators
{ renderColHeaderLabel: () => 'Contributions' },
AddressOverlayMixin,
],
}),
},
});Dropping in an inline object with renderColHeaderLabel is all you need to customize a column's header label.
Built-in ThousandSeparatorPolicyMixin
This built-in mixin from @gridsheet/react-core renders numbers with thousand separators (1,234,567).
import { ThousandSeparatorPolicyMixin } from '@gridsheet/react-core';
new Policy({ mixins: [ThousandSeparatorPolicyMixin] })Locking Column Count with limits
When the number of columns is fixed by the data source, use the limits option to set equal minCols and maxCols. This prevents users from accidentally adding or removing columns.
options={{
mode: 'dark',
sheetHeight: 500,
sheetWidth: 1000,
limits: { minCols: 5, maxCols: 5 },
}}Setting mode: 'dark' switches the entire grid to a dark theme with no additional configuration.
Async Data Fetching and Loading State
Make the API call inside an immediately-invoked async function (IIFE) within useEffect, and toggle rendering with a loading state.
React.useEffect(() => {
(async () => {
try {
setLoading(true);
const response = await request('GET /repos/{owner}/{repo}/contributors', { ... });
setData(oa2aa(response.data as any[], fields));
} catch (error) {
console.error('Failed to fetch data:', error);
} finally {
setLoading(false);
}
})();
}, []);Calling setLoading(false) in finally ensures the loading indicator disappears even when an error occurs.
Cell Address Overlay with renderCallback
This example demonstrates how to display the cell address (e.g. A1, B2) in the top-right corner of every cell using a custom Policy with renderCallback.
This pattern is the recommended replacement for the showAddress option that was available in v2 but removed in v3.
Each cell displays its address (e.g. A1) in the top-right corner using a renderCallback policy.
How It Works
The key is the renderCallback hook on PolicyMixinType. It receives:
renderedโ the output already produced by the policy's type-specific render method (e.g.renderString,renderNumber)propsโ aRenderPropsobject includingpoint: { y, x }
Use p2a(point) (exported from @gridsheet/react-core) to convert the coordinates to a human-readable address string.
Why renderCallback?
renderCallback is called after the primary render method resolves the value. This makes it ideal for decorating cells with metadata that is independent of the cell's value type โ such as an address badge, a status icon, or a tooltip trigger.
Applying Only to Specific Cells
If you only want the overlay on certain cells (rather than all cells via default), assign the policy per cell or per column:
const initialCells = buildInitialCells({
cells: {
// Only column A shows addresses
A: { policy: 'address' },
A1: { value: 'Alice' },
A2: { value: 'Bob' },
},
});๐ Advanced Features
- Real-time Data Updates: Live data synchronization with external APIs
- Caching Strategies: Efficient data caching and refresh mechanisms
- Error Handling: Robust error handling for API failures
- Loading States: Professional loading indicators and states
- Data Validation: Input validation and data integrity checks
๐ API Integration Patterns
- RESTful APIs: Standard REST API integration patterns
- Authentication: Handle API authentication and authorization
- Caching: Implement data caching for improved performance
- Pagination: Handle large datasets with pagination
- Webhooks: Real-time data updates using webhooks