Skip to content

Instantly share code, notes, and snippets.

@nite
Created March 5, 2020 21:06
Show Gist options
  • Save nite/36627e2b0359ddff8d50255da05238d1 to your computer and use it in GitHub Desktop.
Save nite/36627e2b0359ddff8d50255da05238d1 to your computer and use it in GitHub Desktop.
ag-grid wrapper with persistence of grid column & filter state in localStorage
import {ColDef, ColGroupDef, ColumnApi, GridApi} from 'ag-grid-community';
import {AgGridReact} from 'ag-grid-react';
import {AgGridReactProps} from 'ag-grid-react/lib/agGridReact';
import {functions, isEqual, omit} from 'lodash';
import log from 'loglevel';
import React, {useState} from 'react';
import useDeepCompareEffect from 'use-deep-compare-effect'
const gridStateChangeEvents = ['model', 'displayedColumnsChanged'];
export function getFromStorage<T>(storageKey, defaultItem?: T) {
const itemJson = localStorage.getItem(storageKey);
return itemJson ? JSON.parse(itemJson) : defaultItem;
}
export function storeItem<T>(storageKey, item: Nullable<T> = undefined) {
if (item) {
localStorage.setItem(storageKey, JSON.stringify(item));
} else {
localStorage.removeItem(storageKey);
}
}
function arePropsEqual(props, nextProps) {
return isEqual(omit(nextProps, functions(nextProps)), omit(props, functions(props)));
}
interface AgGridWrapperProps extends AgGridReactProps {
name: string;
}
function getColIds(columnDefs) {
const colIds = [
...columnDefs.filter(({colId}: ColDef) => !!colId)
.map(({colId}: ColDef) => colId),
// @ts-ignore
...columnDefs.filter(({children}: ColGroupDef) => children)
.map(({children}: ColGroupDef) => children.map(({colId}: ColDef) => colId))
.flat(),
];
return colIds;
}
export const AgGridWrapper: React.FC<AgGridWrapperProps> = React.memo((props) => {
let {
rowData,
gridOptions,
columnDefs,
name,
} = props;
const storageKey = `grid-state:${name}`;
rowData = gridOptions && !rowData ? gridOptions.rowData : rowData;
columnDefs = gridOptions && !columnDefs ? gridOptions.columnDefs : columnDefs;
const [gridApi, setGridApi] = useState<GridApi>();
const [columnApi, setColumnApi] = useState<ColumnApi>();
useDeepCompareEffect(() => {
if (gridApi && columnApi && rowData) {
gridApi.setRowData(rowData);
}
}, [rowData]);
useDeepCompareEffect(() => {
if (gridApi && columnApi && columnDefs) {
gridApi.setColumnDefs(columnDefs);
const gridState = getFromStorage(storageKey, undefined);
if (!gridState) {
columnApi.autoSizeAllColumns();
}
}
}, [columnDefs]);
async function onGridReady({api, columnApi}) {
setGridApi(api);
setColumnApi(columnApi);
if (!columnDefs) {
return;
}
const gridState = getFromStorage(storageKey, undefined);
if (gridState) {
let {
filter: filterState,
sort: sortState,
column: columnState,
} = gridState;
api.setFilterModel(filterState);
api.setSortModel(sortState);
const colIds = getColIds(columnDefs);
columnState = columnState.filter(({colId}) => colIds.includes(colId));
if (columnState.length) {
columnApi.setColumnState(columnState);
}
} else {
columnApi.autoSizeAllColumns();
}
function persist(type) {
if (gridStateChangeEvents.find(t => type.includes(t))) {
const filter = api.getFilterModel();
const sort = api.getSortModel();
const column = columnApi.getColumnState();
const newState = {
filter,
sort,
column,
};
log.debug('gridStateChange', type, newState);
storeItem(storageKey, newState);
}
}
api.addGlobalListener(persist);
}
return <AgGridReact {...props}
onGridReady={onGridReady}/>
}, arePropsEqual);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment