# Editing

> Learn how to use inline editing to update your data with Infinite Table for React

Canonical page: https://infinite-table.com/docs/learn/editing/overview

By default, editing is not enabled.

To enable editing globally, you can use the [`columnDefaultEditable`](https://infinite-table.com/docs/reference/infinite-table-props.md#columnDefaultEditable) boolean prop on the `InfiniteTable` component. This will enable the editing on all columns.

Or you can be more specific and choose to make individual columns editable via the [column.defaultEditable](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.defaultEditable) prop. This overrides the global [`columnDefaultEditable`](https://infinite-table.com/docs/reference/infinite-table-props.md#columnDefaultEditable).

**Example: Inline Editing in action**

All columns (except id) are editable.

```ts
import {
  InfiniteTable,
  DataSource,
  InfiniteTablePropColumns,
} from '@infinite-table/infinite-react';

import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', defaultWidth: 80, defaultEditable: false },
  stack: {
    field: 'stack',
    contentFocusable: true,
    header: 'Stack',
  },
  firstName: {
    field: 'firstName',
    header: 'Name',
  },
  age: {
    field: 'age',
    header: 'Age',
  },
  hobby: {
    field: 'hobby',
    header: 'Hobby',
  },
  preferredLanguage: {
    header: 'Language',
    field: 'preferredLanguage',
  },
};

export default function ColumnValueGetterExample() {
  return (
    <>
      <DataSource<Developer> primaryKey="id" data={dataSource}>
        <InfiniteTable<Developer>
          debugId="inline-editing-example"
          columns={columns}
          columnDefaultEditable
        />
      </DataSource>
    </>
  );
}
```

Read about how you can configure various editors for your columns.

A picture is worth a thousand words - see a chart for the editing flow.

The [column.defaultEditable](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.defaultEditable) property can be either a `boolean` or a `function`.

If it is a function, it will be called when an edit is triggered on the column. The function will be called with a single object that contains the following properties:

- `value` - the current value of the cell (the value currently displayed, so after [`columns.valueFormatter`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueFormatter) and [`columns.renderValue`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) have been applied)
- `rawValue` - the current value of the cell, but before any formatting and custom rendering has been applied. This is either the field value from the current data object, or the result of the column [valueGetter](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueGetter) function.
- `data` - the data object (of type `DATA_TYPE`) for the current row
- `rowInfo` - the row info object that underlies the row
- `column` - the current column on which editing is invoked
- `api` - a reference to the [InfiniteTable API](https://infinite-table.com/docs/reference/api/index.md)
- `dataSourceApi` - - a reference to the [DataSource API](https://infinite-table.com/docs/reference/datasource-api/index.md)

The function can return a boolean value or a Promise that resolves to a boolean value - this means you can asynchronously decide whether the cell is editable or not.

Making [column.defaultEditable](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.defaultEditable) a function gives you the ability to granularly control which cells are editable or not (even within the same column, based on the cell value or other values you have access to).

In addition to the flags mentioned above, you can use the [`editable`](https://infinite-table.com/docs/reference/infinite-table-props.md#editable) prop on the `InfiniteTable` component. This overrides all other properties and when it is defined, is the only source of truth for whether something is editable or not.

The [`editable`](https://infinite-table.com/docs/reference/infinite-table-props.md#editable) prop allows you to centralize editing logic in one place.

It has the same signature as the [column.defaultEditable](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.defaultEditable) function.

## Start Editing

Editing can be started either by user interaction or programmatically via the [API](https://infinite-table.com/docs/reference/api/index.md).

The user can start editing by double-clicking on a cell or by pressing the `Enter` key while the cell is active (see [Keyboard Navigation for Cells](docs/learn/keyboard-navigation/navigating-cells)).

To start editing programmatically, use the [{`startEdit({ columnId, rowIndex })`}](https://infinite-table.com/docs/reference/api/index.md#startEdit) method.

**Example: Starting an Edit via the API**

```ts
import {
  InfiniteTable,
  DataSource,
  InfiniteTablePropColumns,
  InfiniteTableApi,
} from '@infinite-table/infinite-react';
import { useCallback, useRef, useState } from 'react';

type Developer = {
  id: number;
  firstName: string;
  currency: string;
  stack: string;
  hobby: string;
  salary: string;
};
const dataSource: Developer[] = [
  {
    id: 1,
    firstName: 'John',
    currency: 'USD',
    stack: 'frontend',
    hobby: 'gaming',
    salary: 'USD 1000',
  },
  {
    id: 2,
    firstName: 'Jane',
    currency: 'EUR',
    stack: 'backend',
    hobby: 'reading',
    salary: 'EUR 2000',
  },
  {
    id: 3,
    firstName: 'Jack',
    currency: 'GBP',
    stack: 'frontend',
    hobby: 'gaming',
    salary: 'GBP 3000',
  },
  {
    id: 4,
    firstName: 'Jill',
    currency: 'USD',
    stack: 'backend',
    hobby: 'reading',
    salary: 'USD 4000',
  },
];

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', defaultWidth: 80, defaultEditable: false },

  salary: {
    defaultWidth: 320,
    field: 'salary',
    header: 'Salary - edit accepts numbers only',
    style: { color: 'tomato' },
    getValueToEdit: ({ value }) => {
      return parseInt(value.substr(4), 10);
    },
    getValueToPersist: ({ value, data }) => {
      return `${data!.currency} ${parseInt(value, 10)}`;
    },
    shouldAcceptEdit: ({ value }) => {
      return parseInt(value, 10) == value;
    },
  },

  firstName: {
    field: 'firstName',
    header: 'Name',
  },
  currency: {
    field: 'currency',
    header: 'Currency',
  },
};

export default function InlineEditingExample() {
  const [activeRowIndex, setActiveRowIndex] = useState<number>(2);

  const apiRef = useRef<InfiniteTableApi<Developer> | null>(null);
  const onReady = useCallback(
    ({ api }: { api: InfiniteTableApi<Developer> }) => {
      apiRef.current = api;
    },
    [],
  );
  return (
    <>
      <button
        style={{
          border: '1px solid magenta',
          margin: 2,
          padding: 10,
          background: 'var(--infinite-background)',
          color: 'var(--infinite-cell-color)',
        }}
        onClick={() => {
          apiRef.current!.startEdit({
            rowIndex: activeRowIndex,
            columnId: 'salary',
          });
        }}
      >
        Edit the salary column for active row
      </button>

      <DataSource<Developer> primaryKey="id" data={dataSource}>
        <InfiniteTable<Developer>
          debugId="api-inline-editing-custom-edit-value-example"
          onReady={onReady}
          columns={columns}
          columnDefaultEditable
          activeRowIndex={activeRowIndex}
          onActiveRowIndexChange={setActiveRowIndex}
        />
      </DataSource>
    </>
  );
}
```

Either way, be it user interaction or API call, those actions will trigger checks to see if the cell is editable - taking into account the [`columnDefaultEditable`](https://infinite-table.com/docs/reference/infinite-table-props.md#columnDefaultEditable), [column.defaultEditable](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.defaultEditable) or [`editable`](https://infinite-table.com/docs/reference/infinite-table-props.md#editable) props, as described in the paragraphs above. Only if the result is `true` will the cell editor be displayed.

## Customize Edit Value When Editing Starts

When editing starts, the column editor is displayed with the value that was in the cell. This (initial) edit value can be customized via the [column.getValueToEdit](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.getValueToEdit) prop. This allows you to start editing with a different value than the one that is displayed in the cell - and even with a value fetched asynchronously.

```tsx
const columns = {
  salary: {
    field: 'salary',
    // this can return a Promise
    getValueToEdit: ({ value, data, rowInfo, column }) => {
      // suppose the value is a string like '$1000'
      // but we want to start editing with the number 1000
      return value.replace('$', '');
    },
  },
};
```

**Example: Inline Editing with custom getter for edit value**

Try editing the salary column - it has a custom getter for the edit value, which removes the curency string.

```ts
import {
  InfiniteTable,
  DataSource,
  InfiniteTablePropColumns,
} from '@infinite-table/infinite-react';

type Developer = {
  id: number;
  firstName: string;
  currency: string;
  stack: string;
  hobby: string;
  salary: string;
};
const dataSource: Developer[] = [
  {
    id: 1,
    firstName: 'John',
    currency: 'USD',
    stack: 'frontend',
    hobby: 'gaming',
    salary: 'USD 1000',
  },
  {
    id: 2,
    firstName: 'Jane',
    currency: 'EUR',
    stack: 'backend',
    hobby: 'reading',
    salary: 'EUR 2000',
  },
  {
    id: 3,
    firstName: 'Jack',
    currency: 'GBP',
    stack: 'frontend',
    hobby: 'gaming',
    salary: 'GBP 3000',
  },
  {
    id: 4,
    firstName: 'Jill',
    currency: 'USD',
    stack: 'backend',
    hobby: 'reading',
    salary: 'USD 4000',
  },
];

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', defaultWidth: 80, defaultEditable: false },

  salary: {
    defaultWidth: 320,
    field: 'salary',
    header: 'Salary - edit accepts numbers only',
    style: { color: 'tomato' },
    getValueToEdit: ({ value }) => {
      return parseInt(value.substr(4), 10);
    },
    getValueToPersist: ({ value, data }) => {
      return `${data!.currency} ${parseInt(value, 10)}`;
    },
    shouldAcceptEdit: ({ value }) => {
      return parseInt(value, 10) == value;
    },
  },

  firstName: {
    field: 'firstName',
    header: 'Name',
  },
  currency: {
    field: 'currency',
    header: 'Currency',
  },
};

export default function InlineEditingExample() {
  return (
    <>
      <DataSource<Developer> primaryKey="id" data={dataSource}>
        <InfiniteTable<Developer>
          debugId="inline-editing-custom-edit-value-example"
          columns={columns}
          columnDefaultEditable
        />
      </DataSource>
    </>
  );
}
```

## Finishing an Edit

An edit is generally finished by user interaction - either the user confirms the edit by pressing the `Enter` key or cancels it by pressing the `Escape` key.

As soon as the edit is confirmed by the user, the `InfiniteTable` needs to decide whether the edit should be accepted or not.

In order to decide (either synchronously or asynchronously) whether an edit should be accepted or not, you can use the global [`shouldAcceptEdit`](https://infinite-table.com/docs/reference/infinite-table-props.md#shouldAcceptEdit) prop or the column-level [column.shouldAcceptEdit](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.shouldAcceptEdit) alternative.

When neither the global [`shouldAcceptEdit`](https://infinite-table.com/docs/reference/infinite-table-props.md#shouldAcceptEdit) nor the column-level [column.shouldAcceptEdit](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.shouldAcceptEdit) are defined, all edits are accepted by default.

Once an edit is accepted, the [`onEditAccepted`](https://infinite-table.com/docs/reference/infinite-table-props.md#onEditAccepted) callback prop is called, if defined.

When an edit is rejected, the [`onEditRejected`](https://infinite-table.com/docs/reference/infinite-table-props.md#onEditRejected) callback prop is called instead.

The accept/reject status of an edit is decided by using the `shouldAcceptEdit` props described above. However an edit can also be cancelled by the user pressing the `Escape` key in the cell editor - to be notified of this, use the [`onEditCancelled`](https://infinite-table.com/docs/reference/infinite-table-props.md#onEditCancelled) callback prop.

**Example: Using shouldAcceptEdit to decide whether a value is acceptable or not**

In this example, the `salary` column is configured with a [shouldAcceptEdit](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.shouldAcceptEdit) function property that rejects non-numeric values.

```ts
import {
  InfiniteTable,
  DataSource,
  InfiniteTablePropColumns,
} from '@infinite-table/infinite-react';

type Developer = {
  id: number;
  firstName: string;
  currency: string;
  stack: string;
  hobby: string;
  salary: string;
};
const dataSource: Developer[] = [
  {
    id: 1,
    firstName: 'John',
    currency: 'USD',
    stack: 'frontend',
    hobby: 'gaming',
    salary: 'USD 1000',
  },
  {
    id: 2,
    firstName: 'Jane',
    currency: 'EUR',
    stack: 'backend',
    hobby: 'reading',
    salary: 'EUR 2000',
  },
  {
    id: 3,
    firstName: 'Jack',
    currency: 'GBP',
    stack: 'frontend',
    hobby: 'gaming',
    salary: 'GBP 3000',
  },
  {
    id: 4,
    firstName: 'Jill',
    currency: 'USD',
    stack: 'backend',
    hobby: 'reading',
    salary: 'USD 4000',
  },
];

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', defaultWidth: 80, defaultEditable: false },

  salary: {
    defaultWidth: 320,
    field: 'salary',
    header: 'Salary - edit accepts numbers only',
    style: { color: 'tomato' },
    getValueToEdit: ({ value }) => {
      return parseInt(value.substr(4), 10);
    },
    getValueToPersist: ({ value, data }) => {
      return `${data!.currency} ${parseInt(value, 10)}`;
    },
    shouldAcceptEdit: ({ value }) => {
      return parseInt(value, 10) == value;
    },
  },

  firstName: {
    field: 'firstName',
    header: 'Name',
  },
  currency: {
    field: 'currency',
    header: 'Currency',
  },
};

export default function InlineEditingExample() {
  return (
    <>
      <DataSource<Developer> primaryKey="id" data={dataSource}>
        <InfiniteTable<Developer>
          debugId="inline-editing-custom-edit-value-example"
          columns={columns}
          columnDefaultEditable
        />
      </DataSource>
    </>
  );
}
```

## Persisting an Edit

By default, accepted edits are persisted to the `DataSource` via the [DataSourceAPI.updateData](https://infinite-table.com/docs/reference/datasource-api/index.md#updateData) method.

To change how you persist values (which might include persisting to remote locations), use the [`persistEdit`](https://infinite-table.com/docs/reference/infinite-table-props.md#persistEdit) function prop on the `InfiniteTable` component.

The [`persistEdit`](https://infinite-table.com/docs/reference/infinite-table-props.md#persistEdit) function prop can return a `Promise` for async persistence. To signal that the persisting failed, reject the promise or resolve it with an `Error` object.

After persisting the edit, if all went well, the [`onEditPersistSuccess`](https://infinite-table.com/docs/reference/infinite-table-props.md#onEditPersistSuccess) callback prop is called. If the persisting failed (was rejected), the [`onEditPersistError`](https://infinite-table.com/docs/reference/infinite-table-props.md#onEditPersistError) callback prop is called instead.
