I have grid using ag-grid reactjs typescript that has 3 columns. The 3rd column named "Value" is editable. I was able to edit the value but when onblur event, I lost the edited value and the value is reverted to original. I want to update the value based on I have entered. For example the original value is Initial Value 1 and I edited it to Initial Value 1 Edited, when I leave my mouse or click away, the value of that cell should be Initial Value 1 Edited. However that's not what's happening. It always returns to Initial Value 1. Below is my code. Please help
import React, { useState, useRef, useEffect } from 'react';
import { AgGridReact,ColDef } from 'ag-grid-react';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import { ICellEditorParams } from 'ag-grid-community';
interface RowData {
id: number;
name: string;
value: string;
}
const InputCellEditor = (props: ICellEditorParams<RowData, string>) => {
const inputRef = useRef<HTMLInputElement>(null);
const [value, setValue] = useState(props.value);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
const onKeyDown = (event: React.KeyboardEvent) => {
if (event.key === 'Enter' || event.key === 'Escape') {
event.stopPropagation();
}
};
const getValue = () => {
return value;
};
return (
<input
type="text"
ref={inputRef}
style={{ width: '100%' }}
value={value}
onChange={onChange}
onKeyDown={onKeyDown}
/>
);
};
const GridExample: React.FC = () => {
const gridRef = useRef<AgGridReact>(null);
const [rowData, setRowData] = useState<RowData[]>([
{ id: 1, name: 'Item 1', value: 'Initial Value 1' },
{ id: 2, name: 'Item 2', value: 'Initial Value 2' },
{ id: 3, name: 'Item 3', value: 'Initial Value 3' },
]);
const [colDefs] = useState<ColDef<any>[]>([
{ field:"id", editable:false},
{ field:"name", editable:false},
{ field:"value",
editable:true,
cellEditor:InputCellEditor}
]);
const onCellValueChanged = (params: any) => {
const updatedRowData = rowData.map((item) =>
item.id === params.data.id ? { ...item, [params.colDef.field]: params.newValue } : item
);
setRowData(updatedRowData);
};
return (
<div className="ag-theme-alpine" style={{ height: 300, width: 600 }}>
<AgGridReact
ref={gridRef}
rowData={rowData}
onCellValueChanged={onCellValueChanged}
editType='fullRow'
columnDefs={colDefs}
>
</AgGridReact>
</div>
);
};
export default GridExample;
You need to use forwardRef and useImperativeHandle in your custom cell editor to expose the getValue method to ag-Grid. This way, ag-Grid can call getValue to retrieve the updated value after editing, preventing the value from reverting to the original.
import React, { useState, useRef, useEffect, forwardRef, useImperativeHandle } from 'react';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
const InputCellEditor = forwardRef((props, ref) => {
const inputRef = useRef(null);
const [value, setValue] = useState(props.value);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
useImperativeHandle(ref, () => ({
getValue() {
return value;
}
}));
const onChange = (event) => {
setValue(event.target.value);
};
const onKeyDown = (event) => {
if (event.key === 'Enter' || event.key === 'Escape') {
event.stopPropagation();
}
};
return (
<input
type="text"
ref={inputRef}
style={{ width: '100%' }}
value={value}
onChange={onChange}
onKeyDown={onKeyDown}
/>
);
});
const App = () => {
const gridRef = useRef(null);
const [rowData, setRowData] = useState([
{ id: 1, name: 'Item 1', value: 'Initial Value 1' },
{ id: 2, name: 'Item 2', value: 'Initial Value 2' },
{ id: 3, name: 'Item 3', value: 'Initial Value 3' },
]);
const [columnDefs] = useState([
{ field: 'id', editable: false },
{ field: 'name', editable: false },
{ field: 'value', editable: true, cellEditor: InputCellEditor },
]);
const onCellValueChanged = (params) => {
const updatedRowData = rowData.map((item) =>
item.id === params.data.id
? { ...item, [params.colDef.field]: params.newValue }
: item
);
setRowData(updatedRowData);
};
return (
<div className="ag-theme-alpine" style={{ height: 300, width: 600 }}>
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
onCellValueChanged={onCellValueChanged}
/>
</div>
);
};
export default App;
Update:
I realized that the above code works correctly with these versions: "ag-grid-community": "^30.0.0", "ag-grid-react": "^30.0.0" Here’s how you can update your custom cell editor to work with version 32:
const InputCellEditor = (props, ref) => {
const inputRef = useRef(null);
const [value, setValue] = useState(props.value);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
const onChange = (e) => {
const newValue = e.target.value;
setValue(newValue);
props.onValueChange?.(newValue);
};
const onKeyDown = (e) => {
if (e.key === 'Enter') {
props.stopEditing(false);
e.stopPropagation();
} else if (e.key === 'Escape') {
props.stopEditing(true);
e.stopPropagation();
}
};
const handleBlur = () => {
props.stopEditing(false);
};
return (
<input
type="text"
ref={inputRef}
style={{ width: '100%' }}
value={value}
onChange={onChange}
onKeyDown={onKeyDown}
onBlur={handleBlur}
/>
);
};