([]);
+ const [cellContext, setCellContext] = useState({
+ data,
+ isLoading: false,
+ });
+
+ // Mock fetching data from an async API
+ const mockLoading = useCallback(() => {
+ setCellContext((context) => ({
+ ...context,
+ isLoading: true,
+ }));
+
+ // End the loading state after 3 seconds
+ const timeout = setTimeout(() => {
+ setCellContext((context) => ({
+ ...context,
+ isLoading: false,
+ }));
+ }, 3000);
+ return () => clearTimeout(timeout);
+ }, []);
+
+ const fetchData = useCallback(() => {
+ mockLoading();
+
+ const data: DataType = [];
+ for (let i = 1; i < 5; i++) {
+ data.push({
+ firstName: faker.person.firstName(),
+ lastName: faker.person.lastName(),
+ suffix: faker.person.suffix(),
+ boolean: `${faker.datatype.boolean()}`,
+ });
+ }
+ setData(data);
+ setCellContext((context) => ({ ...context, data }));
+ }, [mockLoading]);
+
+ // Fetch data on page load
+ useEffect(() => {
+ fetchData();
+ }, [fetchData]);
+
+ return (
+ <>
+
+ Fetch grid data
+
+
+
+ >
+ );
+};
diff --git a/src-docs/src/views/datagrid/cells_popovers/datagrid_cells_example.js b/src-docs/src/views/datagrid/cells_popovers/datagrid_cells_example.js
index afa8a3bc6db..1b5352f3192 100644
--- a/src-docs/src/views/datagrid/cells_popovers/datagrid_cells_example.js
+++ b/src-docs/src/views/datagrid/cells_popovers/datagrid_cells_example.js
@@ -21,6 +21,9 @@ import { DataGridCellPopoverExample } from './datagrid_cell_popover_example';
import DataGridFocus from './focus';
const dataGridFocusSource = require('!!raw-loader!./focus');
+import CellContext from './cell_context';
+const cellContextSource = require('!!raw-loader!./cell_context');
+
import {
EuiDataGridColumn,
EuiDataGridColumnCellAction,
@@ -218,5 +221,32 @@ export const DataGridCellsExample = {
),
demo: ,
},
+ {
+ title: 'Cell context',
+ source: [
+ {
+ type: GuideSectionTypes.TSX,
+ code: cellContextSource,
+ },
+ ],
+ text: (
+ <>
+
+ The cellContext prop is an easy way of passing
+ your custom data or context from the top level of{' '}
+ EuiDataGrid down to the cell content rendered by
+ your renderCellValue function component.
+
+
+ The primary use of the cell context API is performance: if your data
+ relies on state from your app, it allows you to more easily define
+ your renderCellValue function statically, instead
+ of within your app, which in turn reduces the number of rerenders
+ within your data grid.
+
+ >
+ ),
+ demo: ,
+ },
],
};
diff --git a/src/components/datagrid/body/cell/data_grid_cell.tsx b/src/components/datagrid/body/cell/data_grid_cell.tsx
index 357edb444ff..059a2f90493 100644
--- a/src/components/datagrid/body/cell/data_grid_cell.tsx
+++ b/src/components/datagrid/body/cell/data_grid_cell.tsx
@@ -59,6 +59,7 @@ const EuiDataGridCellContent: FunctionComponent<
> = memo(
({
renderCellValue,
+ cellContext,
column,
setCellContentsRef,
rowIndex,
@@ -99,6 +100,7 @@ const EuiDataGridCellContent: FunctionComponent<
rowIndex={rowIndex}
colIndex={colIndex}
schema={column?.schema || rest.columnType}
+ {...cellContext}
{...rest}
/>
@@ -465,6 +467,7 @@ export class EuiDataGridCell extends Component<
const {
renderCellPopover,
renderCellValue,
+ cellContext,
rowIndex,
colIndex,
column,
@@ -492,6 +495,7 @@ export class EuiDataGridCell extends Component<
setCellPopoverProps={setCellPopoverProps}
>
= ({
columnWidths,
defaultColumnWidth,
renderCellValue,
+ cellContext,
renderCellPopover,
interactiveCellId,
setRowHeight,
@@ -120,6 +122,7 @@ export const Cell: FunctionComponent = ({
rowManager,
popoverContext,
pagination,
+ cellContext,
};
if (isLeadingControlColumn) {
diff --git a/src/components/datagrid/body/data_grid_body_custom.tsx b/src/components/datagrid/body/data_grid_body_custom.tsx
index d77428bed66..df119b6b84b 100644
--- a/src/components/datagrid/body/data_grid_body_custom.tsx
+++ b/src/components/datagrid/body/data_grid_body_custom.tsx
@@ -38,6 +38,7 @@ export const EuiDataGridBodyCustomRender: FunctionComponent<
schemaDetectors,
visibleRows,
renderCellValue,
+ cellContext,
renderCellPopover,
renderFooterCellValue,
interactiveCellId,
@@ -130,6 +131,7 @@ export const EuiDataGridBodyCustomRender: FunctionComponent<
columnWidths,
defaultColumnWidth,
renderCellValue,
+ cellContext,
renderCellPopover,
interactiveCellId,
setRowHeight,
diff --git a/src/components/datagrid/body/data_grid_body_virtualized.tsx b/src/components/datagrid/body/data_grid_body_virtualized.tsx
index e49b1e9dadd..fc70ed71b3a 100644
--- a/src/components/datagrid/body/data_grid_body_virtualized.tsx
+++ b/src/components/datagrid/body/data_grid_body_virtualized.tsx
@@ -113,6 +113,7 @@ export const EuiDataGridBodyVirtualized: FunctionComponent<
rowCount,
visibleRows: { startRow, endRow, visibleRowCount },
renderCellValue,
+ cellContext,
renderCellPopover,
renderFooterCellValue,
interactiveCellId,
@@ -326,6 +327,7 @@ export const EuiDataGridBodyVirtualized: FunctionComponent<
columnWidths,
defaultColumnWidth,
renderCellValue,
+ cellContext,
renderCellPopover,
interactiveCellId,
rowHeightsOptions,
diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx
index c0fd34833b7..367a157a13a 100644
--- a/src/components/datagrid/data_grid.test.tsx
+++ b/src/components/datagrid/data_grid.test.tsx
@@ -9,7 +9,7 @@
import React, { useEffect, useState } from 'react';
import { mount, ReactWrapper } from 'enzyme';
import { EuiDataGrid } from './';
-import { EuiDataGridProps } from './data_grid_types';
+import type { EuiDataGridProps, RenderCellValue } from './data_grid_types';
import { findTestSubject, requiredProps } from '../../test';
import { render } from '../../test/rtl';
import { EuiDataGridColumnResizer } from './body/header/data_grid_column_resizer';
@@ -976,6 +976,42 @@ describe('EuiDataGrid', () => {
]
`);
});
+
+ it('passes `cellContext` as props to the renderCellValue component', () => {
+ const dataGridProps = {
+ 'aria-label': 'test',
+ columns: [{ id: 'Column' }],
+ columnVisibility: {
+ visibleColumns: ['Column'],
+ setVisibleColumns: () => {},
+ },
+ rowCount: 1,
+ };
+
+ const RenderCellValueWithContext: RenderCellValue = ({ someContext }) => (
+
+ {someContext ? 'hello' : 'world'}
+
+ );
+
+ const { getByTestSubject, rerender } = render(
+
+ );
+ expect(getByTestSubject('renderedCell')).toHaveTextContent('hello');
+
+ rerender(
+
+ );
+ expect(getByTestSubject('renderedCell')).toHaveTextContent('world');
+ });
});
describe('pagination', () => {
diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx
index 9344454f317..b8d9bcc75ec 100644
--- a/src/components/datagrid/data_grid.tsx
+++ b/src/components/datagrid/data_grid.tsx
@@ -110,6 +110,7 @@ export const EuiDataGrid = memo(
schemaDetectors,
rowCount,
renderCellValue,
+ cellContext,
renderCellPopover,
renderFooterCellValue,
className,
@@ -443,6 +444,7 @@ export const EuiDataGrid = memo(
schemaDetectors={allSchemaDetectors}
pagination={pagination}
renderCellValue={renderCellValue}
+ cellContext={cellContext}
renderCellPopover={renderCellPopover}
renderFooterCellValue={renderFooterCellValue}
rowCount={rowCount}
diff --git a/src/components/datagrid/data_grid_types.ts b/src/components/datagrid/data_grid_types.ts
index 7a082117e13..8608552e6c0 100644
--- a/src/components/datagrid/data_grid_types.ts
+++ b/src/components/datagrid/data_grid_types.ts
@@ -268,6 +268,12 @@ export type CommonGridProps = CommonProps &
* as its only argument.
*/
renderCellValue: EuiDataGridCellProps['renderCellValue'];
+ /**
+ * An optional object of props passed to the `renderCellValue` component.
+ * This API exists to make it easier to define your `renderCellValue` function
+ * component statically, and not rerender due to other dependent state.
+ */
+ cellContext?: EuiDataGridCellProps['cellContext'];
/**
* An optional function that can be used to completely customize the rendering of cell popovers.
*
@@ -453,6 +459,7 @@ export interface EuiDataGridBodyProps {
rowCount: number;
visibleRows: EuiDataGridVisibleRows;
renderCellValue: EuiDataGridCellProps['renderCellValue'];
+ cellContext?: EuiDataGridCellProps['cellContext'];
renderCellPopover?: EuiDataGridCellProps['renderCellPopover'];
renderFooterCellValue?: EuiDataGridCellProps['renderCellValue'];
renderCustomGridBody?: EuiDataGridProps['renderCustomGridBody'];
@@ -597,6 +604,16 @@ export interface EuiDataGridCellPopoverElementProps
) => void;
}
+type CellContext = Omit<
+ Record,
+ keyof EuiDataGridCellValueElementProps
+>;
+type CellPropsWithContext = CellContext & EuiDataGridCellValueElementProps;
+
+export type RenderCellValue =
+ | ((props: CellPropsWithContext) => ReactNode)
+ | ComponentClass;
+
export interface EuiDataGridCellProps {
rowIndex: number;
visibleRowIndex: number;
@@ -609,9 +626,8 @@ export interface EuiDataGridCellProps {
isExpandable: boolean;
className?: string;
popoverContext: DataGridCellPopoverContextShape;
- renderCellValue:
- | ((props: EuiDataGridCellValueElementProps) => ReactNode)
- | ComponentClass;
+ renderCellValue: RenderCellValue;
+ cellContext?: CellContext;
renderCellPopover?:
| JSXElementConstructor
| ((props: EuiDataGridCellPopoverElementProps) => ReactNode);