Helper functions for component integration and grid interaction management, providing utilities for accessing React components from grid wrappers and managing reactive component warnings.
Utility function to retrieve the underlying React component from a grid component wrapper instance.
/**
* Function to retrieve the React component from an instance returned by the grid.
* @template TGridComponent - Type of grid component (filter, tool panel, cell editor, status panel)
* @template TCustomComponent - Type of the underlying React custom component
* @param wrapperComponent - Instance component from the grid
* @param callback - Callback which is provided the underlying React custom component
*/
function getInstance<
TGridComponent extends IFilter | FilterDisplay | IToolPanel | ICellEditor | IStatusPanel =
| IFilter
| FilterDisplay
| IToolPanel
| ICellEditor
| IStatusPanel,
TCustomComponent extends TGridComponent = TGridComponent,
>(wrapperComponent: TGridComponent, callback: (customComponent: TCustomComponent | undefined) => void): void;Usage Examples:
import { getInstance } from 'ag-grid-react';
import type { IFilter } from 'ag-grid-community';
// Example with a filter component
const handleFilterInteraction = () => {
const filterInstance = gridApi.getFilterInstance('myColumn') as IFilter;
getInstance(filterInstance, (reactComponent) => {
if (reactComponent) {
// Now you have access to the React component instance
console.log('React filter component:', reactComponent);
// You can call custom methods on your React component
if ('customMethod' in reactComponent) {
(reactComponent as any).customMethod();
}
}
});
};
// Example with a tool panel component
const handleToolPanelInteraction = () => {
const toolPanelInstance = gridApi.getToolPanelInstance('customToolPanel');
getInstance(toolPanelInstance, (reactComponent) => {
if (reactComponent) {
console.log('React tool panel component:', reactComponent);
// Access React component state or methods
if ('getState' in reactComponent) {
const state = (reactComponent as any).getState();
console.log('Tool panel state:', state);
}
}
});
};
// Example with a cell editor component
const handleCellEditorInteraction = (params: any) => {
const cellEditorInstance = params.api.getCellEditorInstance(params.node, params.column);
getInstance(cellEditorInstance, (reactComponent) => {
if (reactComponent) {
console.log('React cell editor component:', reactComponent);
// Trigger custom validation or formatting
if ('validate' in reactComponent) {
const isValid = (reactComponent as any).validate();
console.log('Editor validation result:', isValid);
}
}
});
};Advanced Usage with TypeScript:
import React, { useImperativeHandle, forwardRef } from 'react';
import { getInstance } from 'ag-grid-react';
import type { IFilter, IFilterParams } from 'ag-grid-community';
// Define interface for your custom React component
interface CustomFilterRef {
reset(): void;
getFilterData(): any;
setHighlightMode(enabled: boolean): void;
}
// Custom React filter component with ref
const CustomFilter = forwardRef<CustomFilterRef, IFilterParams>((props, ref) => {
const [filterValue, setFilterValue] = React.useState('');
const [highlightMode, setHighlightMode] = React.useState(false);
useImperativeHandle(ref, () => ({
reset: () => {
setFilterValue('');
},
getFilterData: () => {
return { value: filterValue, timestamp: Date.now() };
},
setHighlightMode: (enabled: boolean) => {
setHighlightMode(enabled);
}
}));
return (
<div style={{ backgroundColor: highlightMode ? '#ffffcc' : 'white' }}>
<input
value={filterValue}
onChange={(e) => setFilterValue(e.target.value)}
placeholder="Custom filter..."
/>
</div>
);
});
// Usage with properly typed getInstance
const interactWithTypedFilter = () => {
const filterInstance = gridApi.getFilterInstance('myColumn') as IFilter;
getInstance<IFilter, CustomFilterRef>(filterInstance, (reactComponent) => {
if (reactComponent) {
// TypeScript now knows about the CustomFilterRef interface
reactComponent.setHighlightMode(true);
setTimeout(() => {
const data = reactComponent.getFilterData();
console.log('Filter data:', data);
reactComponent.reset();
}, 2000);
}
});
};Utility function to warn about reactive custom components usage patterns that may cause performance issues.
/**
* Function to warn about reactive custom components usage patterns.
* Triggers AG Grid warning 231 for reactive custom components.
*/
function warnReactiveCustomComponents(): void;Usage Example:
import { warnReactiveCustomComponents } from 'ag-grid-react';
// Call this function when you detect problematic reactive patterns
const MyProblematicComponent: React.FC = () => {
const [data, setData] = useState([]);
// This pattern might cause performance issues
const columnDefs = useMemo(() => [
{
field: 'name',
cellRenderer: (params: any) => {
// This creates a new component instance on every render
return <span>{params.value.toUpperCase()}</span>;
}
}
], [data]); // Dependencies that change frequently
// Warn about potential issues
useEffect(() => {
if (data.length > 1000) {
warnReactiveCustomComponents();
}
}, [data]);
return <AgGridReact columnDefs={columnDefs} rowData={data} />;
};
// Better pattern - define components outside render or use stable references
const NameCellRenderer: React.FC<any> = ({ value }) => (
<span>{value.toUpperCase()}</span>
);
const MyOptimizedComponent: React.FC = () => {
const [data, setData] = useState([]);
// This is stable and won't cause re-renders
const columnDefs = useMemo(() => [
{
field: 'name',
cellRenderer: NameCellRenderer
}
], []); // No dependencies
return <AgGridReact columnDefs={columnDefs} rowData={data} />;
};Examples showing how to use utility functions in real-world scenarios:
import React, { useRef, useCallback } from 'react';
import { AgGridReact, getInstance } from 'ag-grid-react';
import type { GridApi, ColumnApi } from 'ag-grid-community';
interface GridWithUtilitiesProps {
rowData: any[];
columnDefs: any[];
}
const GridWithUtilities: React.FC<GridWithUtilitiesProps> = ({ rowData, columnDefs }) => {
const gridRef = useRef<AgGridReact>(null);
const [gridApi, setGridApi] = useState<GridApi | null>(null);
const [columnApi, setColumnApi] = useState<ColumnApi | null>(null);
const onGridReady = useCallback((params: { api: GridApi; columnApi: ColumnApi }) => {
setGridApi(params.api);
setColumnApi(params.columnApi);
}, []);
// Example: Reset all custom filters
const resetAllCustomFilters = useCallback(() => {
if (!gridApi) return;
columnDefs.forEach(colDef => {
if (colDef.filter && typeof colDef.filter !== 'string') {
const filterInstance = gridApi.getFilterInstance(colDef.field);
if (filterInstance) {
getInstance(filterInstance, (reactComponent) => {
if (reactComponent && 'reset' in reactComponent) {
(reactComponent as any).reset();
}
});
}
}
});
}, [gridApi, columnDefs]);
// Example: Get data from all custom components
const exportCustomComponentData = useCallback(() => {
if (!gridApi) return {};
const componentData: Record<string, any> = {};
// Export filter data
columnDefs.forEach(colDef => {
if (colDef.filter && typeof colDef.filter !== 'string') {
const filterInstance = gridApi.getFilterInstance(colDef.field);
if (filterInstance) {
getInstance(filterInstance, (reactComponent) => {
if (reactComponent && 'getFilterData' in reactComponent) {
componentData[`filter_${colDef.field}`] = (reactComponent as any).getFilterData();
}
});
}
}
});
// Export tool panel data
const toolPanelInstance = gridApi.getToolPanelInstance('customToolPanel');
if (toolPanelInstance) {
getInstance(toolPanelInstance, (reactComponent) => {
if (reactComponent && 'getState' in reactComponent) {
componentData.toolPanel = (reactComponent as any).getState();
}
});
}
return componentData;
}, [gridApi, columnDefs]);
// Example: Batch update custom components
const updateCustomComponents = useCallback((updates: Record<string, any>) => {
if (!gridApi) return;
Object.entries(updates).forEach(([componentId, updateData]) => {
if (componentId.startsWith('filter_')) {
const fieldName = componentId.replace('filter_', '');
const filterInstance = gridApi.getFilterInstance(fieldName);
if (filterInstance) {
getInstance(filterInstance, (reactComponent) => {
if (reactComponent && 'updateData' in reactComponent) {
(reactComponent as any).updateData(updateData);
}
});
}
}
});
}, [gridApi]);
return (
<div>
<div style={{ marginBottom: '10px' }}>
<button onClick={resetAllCustomFilters}>Reset Filters</button>
<button onClick={() => console.log(exportCustomComponentData())}>
Export Component Data
</button>
</div>
<div className="ag-theme-alpine" style={{ height: 400 }}>
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
onGridReady={onGridReady}
/>
</div>
</div>
);
};import { getInstance, warnReactiveCustomComponents } from 'ag-grid-react';
// Safe getInstance usage with error handling
const safeGetInstance = async <T = any>(
wrapperComponent: any,
timeout: number = 5000
): Promise<T | null> => {
return new Promise((resolve) => {
const timeoutId = setTimeout(() => {
console.warn('getInstance timeout reached');
resolve(null);
}, timeout);
try {
getInstance(wrapperComponent, (reactComponent) => {
clearTimeout(timeoutId);
resolve(reactComponent as T);
});
} catch (error) {
clearTimeout(timeoutId);
console.error('Error getting instance:', error);
resolve(null);
}
});
};
// Usage with error handling
const handleFilterWithErrorHandling = async () => {
const filterInstance = gridApi.getFilterInstance('myColumn');
if (!filterInstance) {
console.warn('Filter instance not found');
return;
}
const reactComponent = await safeGetInstance(filterInstance, 3000);
if (reactComponent && 'customMethod' in reactComponent) {
try {
(reactComponent as any).customMethod();
} catch (error) {
console.error('Error calling custom method:', error);
}
}
};
// Performance monitoring with warnings
const monitorComponentPerformance = (componentCount: number) => {
if (componentCount > 100) {
console.warn(`High number of custom components detected: ${componentCount}`);
warnReactiveCustomComponents();
}
};