I've got a table, and each row (DifferentialRow) of the table queries for its own data with react-query's useQuery hook. I want the table's rows to be sorted by a given field value (calculated within DifferentialRow), and also only show the top 10.
<Table.Body>
{filteredVariables!.map((variable, i) => {
return variable.show ? (
<DifferentialRow
key={i}
....
variable={variable.variable}
setFilteredVariables={setFilteredVariables}
/>
) : null;
})}
</Table.Body>
So when a (DifferentialRow) row has retrieved its data and calculated the sort value, I update the parent filteredVariables object with the new row value, sort, and then set show = true for the top 10 using setFilteredVariables which is passed into DifferentialRow (all shown below).
const diffQuery = useQuery(["differential", {startDate, variable}],fetchDifferentialData);
...
if (diffQuery.isSuccess && diffQuery.data) {
setSortValue(calcSortValue(diffQuery.data.data));
}
html rows here
...
function calcSortValue(resultData: any[]) {
// once we've got a result, and we have calculated the diff we need
// to set the filteredVariables object that keeps track of cumulative data to only show top x
try {
let sortValue = resultData[0].dataValue - resultData[numberOfDays - 1].dataValue;
setFilteredVariables((prev: { variable: string; diff: number; show: boolean }[]) => {
let newResults = [...prev, { diff: sortValue, variable, show: undefined }];
newResults.sort((a, b) => {
return Math.abs(b.diff || 0) - Math.abs(a.diff || 0);
});
let inTopTen = newResults
.slice(0, 10)
.map((co) => co.variable)
.includes(variable);
let finalResults: CompareObject[];
if (inTopTen) {
finalResults = newResults.map((nr) => {
return nr.variable === variable? { ...nr, show: true }: nr;
});
} else {
finalResults = newResults.map((nr) => {
return nr.variable === variable? { ...nr, show: false }: nr;
});
}
return finalResults;
});
return diff;
} catch (error) {
return 0;
}
}
This is all creating circular re-rendering, and I can't figure out how to get around it.
Okay, so my solution was to completely remove the DistributionRow component and query for the data in the parent (table) component using useQueries (each query representing a row). Then I do all the sorting and slicing on the result from useQueries.
const diffResults = useQueries(...)
return diffResults.some((dr) => dr.isSuccess) ? .... <Table.Body>
{diffResults
.filter((dr) => dr.isSuccess)
.map((dr: any) => {
let dateSorted = dr.data.data.sort(function (a: any, b: any) {
return new Date(b.runDate).getTime() - new Date(a.runDate).getTime();
});
let diff = Math.round(calcDiff(dateSorted) * 10) / 10;
let fullResult = {
results: dr.data,
diff,
};
return fullResult;
})
.sort((a, b) => {
return Math.abs(b.diff || 0) - Math.abs(a.diff || 0);
})
.slice(0, 10)
.map(({ diff, results }, i) => {
return (
<Table.Row key={i} data-testid="table-row">
<Table.Cell>{results.variable}</Table.Cell>
{results.data.map((d: any, i: number) =>
new Date(d.runDate) > endDate ? (
<Table.Cell key={i}>
{isNaN(d?.dataValue) ? null : Math.round(d?.dataValue * 10) / 10}
</Table.Cell>
) : null
)}
{compareColumn ? <Table.Cell>{diff}</Table.Cell> : null}
</Table.Row>
);
})}
</Table.Body>