Migrate
Version 2 contains breaking changes over version 1. Use the following guide to migrate from version 1 to 2.
RowDef Type Change
Previously, the type that defines the contents of a single row, RowDef consisted of an object containing each of the
fields defined in the column definitions (an array of ColDef). Consider the following example:
const cols: ColDef[] = [
{
type: "string",
name: "strCol",
label: "String Column",
},
{
type: "number",
name: "numCol",
label: "Number Column",
},
{
type: "date",
name: "dateCol",
label: "Date Column",
},
{
type: "datetime",
name: "datetimeCol",
label: "Datetime Column",
},
];
const rows: RowDef[] = [
{
strCol: "String value",
numCol: 1,
dateCol: new Date(),
datetimeCol: new Date(),
},
// additional rows
];In version 2, the ColDefs will remain the same, but the RowDefs will need to have an explicit id defined, the id
can be a number or string and must be unique among all of the rows in the array of RowDefs passed to the rows
prop of the Grid component.
In documentation, the type of id is often referred to by the type RowId, which is number | string.
The id will be used as a key prop for components that render the rows in the Grid. See the relevant React
documentation on selecting keys to get
more insight on selecting a proper key.
Besides potential performance improvements from reducing the frequency of mounting and unmounting components representing rows, this design change is important to avoid race conditions when editing or deleting rows.
The above example can be adapted for version 2 of react-bootstrap-data-grid as follows:
const rows: RowDef[] = [
{
id: 0,
data: {
strCol: "String value",
numCol: 1,
dateCol: new Date(),
datetimeCol: new Date(),
},
},
// additional rows
];One can also specify a generic type for the data field of RowDef to have a code editor and/or TypeScript aid in
typechecking:
interface TestRowData {
strCol: string;
numCol: number;
dateCol: Date;
datetimeCol: Date;
}
const rows: RowDef<TestRowData>[] = [
{
id: 0,
data: {
strCol: "String value",
numCol: 1,
dateCol: new Date(),
datetimeCol: new Date(),
},
},
// additional rows
];Note that it is the responsibility of the developer to specify this generic type in a way that lines up with the column
definitions. If RowDefs are not compatible with the ColDefs, the editor may give no indication of this fact, and the
developer may not find out until they actually run the code and encounter Errors from the Grid component.
Editing Feature Changes
The callback generator functions passed to the editModel are now based on id rather than the index of the original
array. One can adapt existing callback generator functions to work with version 2 by searching for rows by id.
Consider the following example that works with version 1:
const EditingTestHarness: FC = () => {
const [rows, setRows] = useState<RowDef[]>(initRows.slice());
const getUpdateCallback: UpdateCallbackGenerator =
(origIndex) => (rowDef) => {
const newRows = rows.slice();
newRows[origIndex] = rowDef;
setRows(newRows);
};
const getDeleteCallback: (origIndex: number) => () => void =
(origIndex) => () => {
setRows(rows.toSpliced(origIndex, 1));
};
return (
<Grid
rows={rows}
cols={cols}
editModel={{ getUpdateCallback, getDeleteCallback }}
/>
);
};It can be adapted to work with version 2 follows:
const EditingTestHarness: FC = () => {
const [rows, setRows] = useState<RowDef[]>(initRows.slice());
const getUpdateCallback: UpdateCallbackGenerator = (id) => (rowData) => {
const newRows = rows.slice();
const index = rows.findIndex((row) => row.id === id);
if (index === undefined) {
return;
}
newRows[index] = {
id,
data: rowData,
};
setRows(newRows);
};
const getDeleteCallback: (id: RowId) => () => void = (id) => () => {
const index = rows.findIndex((row) => row.id === id);
if (index === undefined) {
return;
}
setRows(rows.toSpliced(index, 1));
};
return (
<Grid
rows={rows}
cols={cols}
editModel={{ getUpdateCallback, getDeleteCallback }}
/>
);
};While this new design results in more code in the callback generator functions, it can prevent race conditions that
result in editing or deleting the wrong row, assuming that ids are stable.
Styling Feature Changes
Several members of the TableStyleModel are callback functions that now refer to rows by id rather than the original
index. Consider the code sample involving color variants in the Styling section of this documentation. The
original version of the code affected by changes in version 2 is as follows:
const getRows: (partialRows: PartialRow[], colors: string[]) => RowDef[] = (
partialRows,
colors,
) =>
partialRows.map((_, index) => ({
variant: partialRows[index].variant,
color: colors[index],
alignment: partialRows[index].alignment,
placeholderText: placeholderText,
}));
const tableStyleModel: TableStyleModel = {
caption: ["caption-top"],
table: ["table-bordered", "border-primary"],
tbodyTr: (origIndex) => [`table-${partialRows[origIndex].variant}`],
tbodyTd: (origRowIndex, _, colIndex) =>
colIndex === 2 ? [`align-${partialRows[origRowIndex].alignment}`] : [],
};The example has since been adapted to work with string ids that are the names of the variants (e.g. primary, info,
etc.).
interface Data {
variant: string;
color: string;
alignment: string;
placeholderText: string;
}
const getRows: (
partialRows: PartialRow[],
colors: string[],
) => RowDef<Data>[] = (partialRows, colors) =>
partialRows.map((_, index) => ({
id: partialRows[index].variant,
data: {
variant: partialRows[index].variant,
color: colors[index],
alignment: partialRows[index].alignment,
placeholderText: placeholderText,
},
}));
const variantToAlignment = new Map(
partialRows.map(({ variant, alignment }) => [variant, alignment]),
);
const tableStyleModel: TableStyleModel = {
caption: ["caption-top"],
table: ["table-bordered", "border-primary"],
tbodyTr: (id) => [`table-${id}`],
tbodyTd: (id, _, colIndex) =>
colIndex === 2 ? [`align-${variantToAlignment.get(id as string)}}`] : [],
};Selection Feature Changes
Both the SingleSelectModel and MultiSelectModel now refer to rows by id rather than by index. You can refer to the
external interaction example in the Selection section of this documentation for example of how to adapt
to existing SelectModels to version 2.
The affected sections of code were implemented as follows in version 1:
const ExternalInteractionExample: FC = () => {
const [rows, setRows] = useState<RowDef[]>(origRows);
const [selected, setSelected] = useState<number[]>([]);
const selectModel: MultiSelectModel = useMemo(
() => ({
mode: "both",
type: "multi",
selected,
setSelected,
}),
[selected],
);
const deleteSelectedRows = () => {
setRows(rows.filter((_, index) => !selected.includes(index)));
setSelected([]);
};For version 2, code was adapted to refer to RowId rather than number:
const ExternalInteractionExample: FC = () => {
const [rows, setRows] = useState<RowDef[]>(origRows);
const [selected, setSelected] = useState<RowId[]>([]);
const selectModel: MultiSelectModel = useMemo(
() => ({
mode: "both",
type: "multi",
selected,
setSelected,
}),
[selected],
);
const deleteSelectedRows = () => {
setRows(rows.filter(({ id }) => !selected.includes(id)));
setSelected([]);
};Column Formatter Changes
If one does not specify the formatter property of a ColDef object of type date, datetime, or number, the
Grid uses default formatters as follows:
| Type | Default Formatter |
|---|---|
| date | dateToInputStr |
| datetime | dateToDatetimeInputStr |
| number | Number.prototype.toString() |
They have now been changed to the following for version 2:
| Type | Default Formatter |
|---|---|
| date | Date.prototype.toDateString() |
| datetime | Date.prototype.toLocaleString() |
| number | Number.prototype.toLocaleString() |
Where dateToInputStr and dateToDatetimeInputStr are utility functions that are a part of this project that output
strings used by date and datetime-local <input> elements, respectively.
This change was made for performance considerations in case of number and datetime. For date, the change was made
due to the output of the previous default formatter being very long.
To continue using the original behavior of version 1 in version 2, simply add formatter functions to the respective
ColDefs:
const cols: ColDef[] = [
{
type: "number",
name: "numCol",
label: "Number Column",
formatter: (num: number) => num.toLocaleString(),
},
{
type: "date",
name: "dateCol",
label: "Date Column",
formatter: (date: Date) => date.toDateString(),
},
{
type: "datetime",
name: "datetimeCol",
label: "Datetime Column",
formatter: (datetime: Date) => datetime.toLocaleString(),
},
];