I am struggling to get External Filters to work using useState in reactJS. I have updated your published example to use useState and the filtering is always one click behind the actual filter that has been selected i.e. first select does nothing, second select filters based on the previous selected filter.
It seems that the doesExternalFilterPass function is holding onto the previous state even though the gridApi.onFilterChanged() is being called from a useEffect that does contain the correct state.
https://plnkr.co/edit/0KID6KbOpGyIYcz7
'use strict';
import React, { useState, useEffect, useRef } from 'react';
import { render } from 'react-dom';
import { AgGridReact, AgGridColumn } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
const GridExample = () => {
const [gridApi, setGridApi] = useState(null);
const [gridColumnApi, setGridColumnApi] = useState(null);
const [rowData, setRowData] = useState(null);
// add useState
const [ageType, setAgeType] = useState({value:'everyone'})
const onGridReady = (params) => {
setGridApi(params.api);
setGridColumnApi(params.columnApi);
const updateData = (data) => {
document.querySelector('#everyone').checked = true;
setRowData(data);
};
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data) => updateData(data));
};
// modify to use useState
const externalFilterChanged = (newValue) => {
setAgeType({value : newValue});
};
const isExternalFilterPresent = () => {
return true;
};
// modify to use useState
const doesExternalFilterPass = (node) => {
switch (ageType.value) {
case 'below25':
return node.data.age < 25;
case 'between25and50':
return node.data.age >= 25 && node.data.age <= 50;
case 'above50':
return node.data.age > 50;
case 'dateAfter2008':
return asDate(node.data.date) > new Date(2008, 1, 1);
default:
return true;
}
};
// add useEffect to call onFilterChanged when filter updated
useEffect ( () => {
gridApi && gridApi.onFilterChanged();
}, [ageType] )
return (
<div style={{ width: '100%', height: '100%' }}>
<div className="test-container">
<div className="test-header">
<label>
<input
type="radio"
name="filter"
id="everyone"
onChange={() => externalFilterChanged('everyone')}
/>
Everyone
</label>
<label>
<input
type="radio"
name="filter"
id="below25"
onChange={() => externalFilterChanged('below25')}
/>
Below 25
</label>
<label>
<input
type="radio"
name="filter"
id="between25and50"
onChange={() => externalFilterChanged('between25and50')}
/>
Between 25 and 50
</label>
<label>
<input
type="radio"
name="filter"
id="above50"
onChange={() => externalFilterChanged('above50')}
/>
Above 50
</label>
<label>
<input
type="radio"
name="filter"
id="dateAfter2008"
onChange={() => externalFilterChanged('dateAfter2008')}
/>
After 01/01/2008
</label>
</div>
<div
id="myGrid"
style={{
height: '100%',
width: '100%',
}}
className="ag-theme-alpine"
>
<AgGridReact
defaultColDef={{
flex: 1,
minWidth: 120,
filter: true,
}}
animateRows={true}
isExternalFilterPresent={isExternalFilterPresent}
doesExternalFilterPass={doesExternalFilterPass}
onGridReady={onGridReady}
rowData={rowData}
>
<AgGridColumn field="athlete" minWidth={180} />
<AgGridColumn
field="age"
filter="agNumberColumnFilter"
maxWidth={80}
/>
<AgGridColumn field="country" />
<AgGridColumn field="year" maxWidth={90} />
<AgGridColumn
field="date"
filter="agDateColumnFilter"
filterParams={dateFilterParams}
/>
<AgGridColumn field="gold" filter="agNumberColumnFilter" />
<AgGridColumn field="silver" filter="agNumberColumnFilter" />
<AgGridColumn field="bronze" filter="agNumberColumnFilter" />
</AgGridReact>
</div>
</div>
</div>
);
};
var dateFilterParams = {
comparator: function (filterLocalDateAtMidnight, cellValue) {
var cellDate = asDate(cellValue);
if (filterLocalDateAtMidnight.getTime() === cellDate.getTime()) {
return 0;
}
if (cellDate < filterLocalDateAtMidnight) {
return -1;
}
if (cellDate > filterLocalDateAtMidnight) {
return 1;
}
},
};
function asDate(dateAsString) {
var splitFields = dateAsString.split('/');
return new Date(splitFields[2], splitFields[1], splitFields[0]);
}
render(<GridExample></GridExample>, document.querySelector('#root'));
I have worked around this using useRef, setting the useRef to the useState value in the useEffect function, and then updating the doesExternalFilterPass function to use ref.current.value.
https://plnkr.co/edit/qwY0Qcp2HT59ucoN
'use strict';
import React, { useState, useEffect, useRef } from 'react';
import { render } from 'react-dom';
import { AgGridReact, AgGridColumn } from 'ag-grid-react';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
const GridExample = () => {
const [gridApi, setGridApi] = useState(null);
const [gridColumnApi, setGridColumnApi] = useState(null);
const [rowData, setRowData] = useState(null);
// add useState and useRef
const [ageType, setAgeType] = useState({ value: 'everyone' });
const ageTypeRef = useRef(ageType);
const onGridReady = (params) => {
setGridApi(params.api);
setGridColumnApi(params.columnApi);
const updateData = (data) => {
document.querySelector('#everyone').checked = true;
setRowData(data);
};
fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((resp) => resp.json())
.then((data) => updateData(data));
};
const externalFilterChanged = (newValue) => {
// update to useState
setAgeType({ value: newValue });
};
const isExternalFilterPresent = () => {
// update to useRef
return ageTypeRef.current.value !== 'everyone';
};
const doesExternalFilterPass = (node) => {
// update to useRef
switch (ageTypeRef.current.value) {
case 'below25':
return node.data.age < 25;
case 'between25and50':
return node.data.age >= 25 && node.data.age <= 50;
case 'above50':
return node.data.age > 50;
case 'dateAfter2008':
return asDate(node.data.date) > new Date(2008, 1, 1);
default:
return true;
}
};
// add useEffect to call onFilterChanged when filter updated
useEffect(() => {
// add update useRef variable to current values
ageTypeRef.current = ageType;
gridApi && gridApi.onFilterChanged();
}, [ageType]);
return (
<div style={{ width: '100%', height: '100%' }}>
<div className="test-container">
<div className="test-header">
<label>
<input
type="radio"
name="filter"
id="everyone"
onChange={() => externalFilterChanged('everyone')}
/>
Everyone
</label>
<label>
<input
type="radio"
name="filter"
id="below25"
onChange={() => externalFilterChanged('below25')}
/>
Below 25
</label>
<label>
<input
type="radio"
name="filter"
id="between25and50"
onChange={() => externalFilterChanged('between25and50')}
/>
Between 25 and 50
</label>
<label>
<input
type="radio"
name="filter"
id="above50"
onChange={() => externalFilterChanged('above50')}
/>
Above 50
</label>
<label>
<input
type="radio"
name="filter"
id="dateAfter2008"
onChange={() => externalFilterChanged('dateAfter2008')}
/>
After 01/01/2008
</label>
</div>
<div
id="myGrid"
style={{
height: '100%',
width: '100%',
}}
className="ag-theme-alpine"
>
<AgGridReact
defaultColDef={{
flex: 1,
minWidth: 120,
filter: true,
}}
animateRows={true}
isExternalFilterPresent={isExternalFilterPresent}
doesExternalFilterPass={doesExternalFilterPass}
onGridReady={onGridReady}
rowData={rowData}
>
<AgGridColumn field="athlete" minWidth={180} />
<AgGridColumn
field="age"
filter="agNumberColumnFilter"
maxWidth={80}
/>
<AgGridColumn field="country" />
<AgGridColumn field="year" maxWidth={90} />
<AgGridColumn
field="date"
filter="agDateColumnFilter"
filterParams={dateFilterParams}
/>
<AgGridColumn field="gold" filter="agNumberColumnFilter" />
<AgGridColumn field="silver" filter="agNumberColumnFilter" />
<AgGridColumn field="bronze" filter="agNumberColumnFilter" />
</AgGridReact>
</div>
</div>
</div>
);
};
var dateFilterParams = {
comparator: function (filterLocalDateAtMidnight, cellValue) {
var cellDate = asDate(cellValue);
if (filterLocalDateAtMidnight.getTime() === cellDate.getTime()) {
return 0;
}
if (cellDate < filterLocalDateAtMidnight) {
return -1;
}
if (cellDate > filterLocalDateAtMidnight) {
return 1;
}
},
};
function asDate(dateAsString) {
var splitFields = dateAsString.split('/');
return new Date(splitFields[2], splitFields[1], splitFields[0]);
}
render(<GridExample></GridExample>, document.querySelector('#root'));
Is this the correct approach?
Thanks,