AdapTable - Key Concepts
There are a few important concepts that you need to understand when working with Adaptable:
- configuring data and primary key
- state and state persistence
- views and available columns
- rendering the component
Configuring data and primary key
Obviously any DataGrid component needs data. This is a top-level prop, and your data
can be one of the following:
- an array
- a Promise resolving to an array
- a function returning either of the above
Note
Because of the flexibility of the data
prop, you can load data however you want.
It's easy to use fetch
and pass the resulting promise to the component or you can go with a more robust solution like TanStack Query or SWR.
Adaptable will not mind which solution you choose.
You can also specify a function which we'll call for you. When the data
prop is a function, we'll call it with the current filtering, sorting, grouping and pivoting information of the current view, so you can correctly load the data from your remote location.
For more information on data loading, read the excellent Infinite Table docs to see more examples.
<Adaptable.Provider data={data} primaryKey="id">
...
</Adaptable.Provider>
After specifying the data
, you need to pass the primaryKey
prop. This will be used to uniquely identify each row in your data.
You need to make sure the primary key values are unique, otherwise AdapTable will behave inconsistently. Many features of AdapTable rely on this uniqueness - for example, row selection, cell selection, editing, cell summary to name a few.
Understanding state and persistence
The Adaptable state (passed into the React component via the defaultState
prop) determines how the component will look and behave.
By configuring the default state, you control how the user will initially see the AdapTable component. After the component is initialised, the user will interact with it and almost every interaction will update the state object internally (e.g.: changing column order, sorting, creating a new view).
Note
The Adaptable state is the source of truth for the component. It controls every part of the UI (except the data, which is controlled by the data
prop).
The most important part of the state are the views.
The state.view property is a mandatory property, as you need to have at least one view. The view contains an array of columns, which are references to the columns made available to the component via state.globalEntities.availableColumns
.
The state.globalEntities.availableColumns
is an object keyed by the column id, which contains all the columns that can be used in the views - each column describes the field it's bound to, the data type, whether it's sortable/pinned/groupable and more.
In turn, each view defines an array of columns, which are objects that refer to the available columns via the id. We call those column references.
import { Adaptable, type AdaptableColDef } from '@adaptabletools/adaptable-infinite-react';
// JavaScript Web frameworks
const availableColumns: Record<string, AdaptableColDef> = {
id: { field: 'id', dataType: 'number' },
name: { field: 'name', label: 'Name', dataType: 'text' },
language: { field: 'language', dataType: 'text' },
license: { field: 'license', dataType: 'text' },
stars: {
field: 'github_stars', editable: true, dataType: 'number'
},
'2xstars': { expression: '[github_stars] * 2', dataType: 'number' }
};
<Adaptable.Provider
primaryKey="id"
defaultState={{
view: {
currentViewId: 'tableView',
views: [
{
id: 'tableView', label: 'Table View',
columns: [
{ id: 'name' }, // reference the `name` column
{ id: 'stars', editable: false },
{ id: 'language' },
],
}
]
},
globalEntities: {
availableColumns,
},
}}
adaptableId="Adaptable Demo"
data={[...]}
>
</Adaptable.Provider>
State Persistence
After the component is initialised with the defaultState
prop, AdapTable will manage the state internally and will persist it in the browser's local storage by default.
This behavior can be configured by specifying a custom persistState
function. If you specify a custom persistState
function to change the way the state is persisted (e.g. send it to your application server), you'll also need to pair it with the loadState
function. loadState
will be called when the AdapTable component is initialised and it should fetch the state from the persistence layer (e.g. from your application server) - it will do the exact opposite of persistState
.
<Adaptable.Provider
primaryKey="id"
adaptableId="Basic State"
persistState={async (context) => {
const { state, stateKey } = context;
await fetch(`https://my-server.com/save-state?key=${stateKey}`, {
method: 'POST',
body: JSON.stringify(state),
});
}}
loadState={(context) => {
const { stateKey } = context;
return fetch(`https://my-server.com/load-state?key=${stateKey}`).then((response) =>
response.json(),
);
}}
>
<Adaptable.UI />
</Adaptable.Provider>
Note
You can disable state persistence if you don't want to persist the state at all.
Find Out More
See Adaptable State for full information on using and managing the Adaptable state.
Understanding and configuring views
Views are the cornerstone of Adaptable. Almost everything you see in the DataGrid is controlled by the current view.
Adaptable can be configured with multiple views but only one view can be active at any given time.
The active view determines the visible columns, the column order, the row grouping, the filters, the sorting, which alerts are active, which conditional styles are active and more.
Views are configured via the (mandatory) view
property in the default state. The columns in the view use their id
to reference the available columns in state.globalEntities.availableColumns
.
defaultState = {
// configure Views here
view: {
currentViewId: 'myView',
views: [
{
id: 'myView',
label: 'My View',
columns: [
{
// there's a `full_name` col already defined in the availableColumns
id: 'full_name',
},
{
id: 'description',
},
],
},
],
},
};
Caution
- Developers must always provide at least one View in their State
- Views can be deleted, but there must always be at least one View in the State
Types of Views
AdapTable provides 2 types of views:
-
Table Views - show columns in standard tabular format (can have row grouping, aggregations, etc.)
-
Pivot Views - display data in pivoted, aggregated format
Each View is either a Table View or a Pivot View.
Find Out More
See Our Docs on Views for full information on using views.
Rendering the component
After the data
, primaryKey
, defaultState
and adaptableId
are configured, you're ready to render the component.
To make it flexible, we're exposing the Adaptable state to your app via a context - that's the reason why you need to configure the <Adaptable.Provider />
will all of the above props, but also need to render a separate <Adaptable.UI />
inside it.
Basically you can render any other parts of your app inside the <Adaptable.Provider />
, with the added benefit that you get access to the Adaptable state, via the useAdaptableState()
React hook.
import { Adaptable } from '@adaptabletools/adaptable-infinite-react';
function App() {
return <Adaptable.Provider {...}>
<YourCmp> // <--- has access to useAdaptableState()
<Adaptable.UI />
</Adaptable.Provider>
Deep Dive
Using Adaptable hooks to access the state
Adaptable offers a few hooks that give you access to the Adaptable state, so you can better integrate the component in your app.
useAdaptableState(fn?)
import { useAdaptableState } from '@adaptabletools/adaptable-infinite-react';
You can use this hook to get access to the whole Adaptable state or to a part of it.
Passing the optional fn
parameter is allows you to select which part of the state you're interested in.
For example, you can get the id of the current view or you can get the currently applied theme.
const viewId = useAdaptableState((state) => state.view.currentViewId);
For more details, read the dedicated page.
useAdaptableStateSubscribe(param)
This hook allows you to subscribe to state changes
useAdaptableStateSubscribe({
selector: (state) => state.view.currentViewId,
callback: (viewId, oldViewId) => {
console.log('view changed', viewId);
},
});
The hook should be called with an object that expects the following properties
selector
-(state: AdaptableState) => any
- this function is called with the current Adaptable state and should return the part of the state that you're interested in.callback
-(value, oldValue) => void
- this function is called whenever the value returned by theselector
function changes. It's called with the new value and the old value.
useAdaptableApi()
This hook gives you access to the Adaptable API, which you can use to change the state of the component directly.
For example, you can use the AdaptableApi
to change the current view, the theme, add a new alert or a flashing cell, filter the grid and more.
import { useAdaptableApi } from '@adaptabletools/adaptable-infinite-react';
function App() {
return (
<Adaptable.Provider>
<YourApp />
</Adaptable.Provider>
);
}
function YourApp() {
const api = useAdaptableApi();
const setLightTheme = () => {
api.themeApi.setTheme('light');
};
return (
<>
<CustomCmp />
<button onClick={setLightTheme}>set light theme</button>
<Adaptable.UI />
</>
);
}