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.

Passing data and primaryKey props
<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.

Defining a basic view
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.

Customising persistState and loadState
<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>

Using hooks to get access to Adaptable state in your components
Fork
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.

Getting the id of the currently active view
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

Listening to view change event
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 the selector 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.

Getting a reference to the AdaptableApi
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 />
    </>
  );
}