Fix concurrency bug in results view

In the results view, `setState` was used to set some state, and then
`loadResults` was called to set some other state. However, `setState`
is asynchronous, so the `this.state` in `loadResults` was not the state
that was set before the call.

This commit fixes it by combining the two `setState` calls into one. The
logic was hard to follow, so I'm not sure if this is the correct fix.
The `shouldKeepOldResultsWhileRendering` state seems to be unnecessary
now since everything is being done in `setState` call, but I'm not sure
about that.
This commit is contained in:
Koen Vlaswinkel
2023-03-14 10:22:34 +01:00
parent 605315c70d
commit 36eb2cd2ea

View File

@@ -104,7 +104,6 @@ export class ResultsApp extends React.Component<
queryPath: msg.queryPath,
});
void this.loadResults();
break;
case "showInterpretedPage": {
const tableName =
@@ -141,7 +140,6 @@ export class ResultsApp extends React.Component<
queryName: msg.queryName,
queryPath: msg.queryPath,
});
void this.loadResults();
break;
}
case "resultsUpdating":
@@ -164,49 +162,30 @@ export class ResultsApp extends React.Component<
private updateStateWithNewResultsInfo(resultsInfo: ResultsInfo): void {
this.setState((prevState) => {
const stateWithDisplayedResults = (
displayedResults: ResultsState,
): ResultsViewState => ({
displayedResults,
isExpectingResultsUpdate: prevState.isExpectingResultsUpdate,
nextResultsInfo: resultsInfo,
});
if (!prevState.isExpectingResultsUpdate && resultsInfo === null) {
// No results to display
return stateWithDisplayedResults({
resultsInfo: null,
results: null,
errorMessage: "No results to display",
});
}
if (!resultsInfo || !resultsInfo.shouldKeepOldResultsWhileRendering) {
if (resultsInfo === null) {
if (prevState.isExpectingResultsUpdate) {
// Display loading message
return stateWithDisplayedResults({
return {
displayedResults: {
resultsInfo: null,
results: null,
errorMessage: "Loading results…",
});
},
isExpectingResultsUpdate: prevState.isExpectingResultsUpdate,
nextResultsInfo: resultsInfo,
};
} else {
// No results to display
return {
displayedResults: {
resultsInfo: null,
results: null,
errorMessage: "No results to display",
},
isExpectingResultsUpdate: prevState.isExpectingResultsUpdate,
nextResultsInfo: resultsInfo,
};
}
return stateWithDisplayedResults(prevState.displayedResults);
});
}
private getResultSets(resultsInfo: ResultsInfo): readonly ResultSet[] {
const parsedResultSets = resultsInfo.parsedResultSets;
const resultSet = parsedResultSets.resultSet;
if (!resultSet.t) {
throw new Error(
'Missing result set type. Should be either "InterpretedResultSet" or "RawResultSet".',
);
}
return [resultSet];
}
private async loadResults(): Promise<void> {
const resultsInfo = this.state.nextResultsInfo;
if (resultsInfo === null) {
return;
}
let results: Results | null = null;
@@ -229,11 +208,6 @@ export class ResultsApp extends React.Component<
statusText = `Error loading results: ${errorMessage}`;
}
this.setState((prevState) => {
// Only set state if this results info is still current.
if (resultsInfo !== prevState.nextResultsInfo) {
return null;
}
return {
displayedResults: {
resultsInfo,
@@ -246,6 +220,17 @@ export class ResultsApp extends React.Component<
});
}
private getResultSets(resultsInfo: ResultsInfo): readonly ResultSet[] {
const parsedResultSets = resultsInfo.parsedResultSets;
const resultSet = parsedResultSets.resultSet;
if (!resultSet.t) {
throw new Error(
'Missing result set type. Should be either "InterpretedResultSet" or "RawResultSet".',
);
}
return [resultSet];
}
private getSortStates(
resultsInfo: ResultsInfo,
): Map<string, RawResultsSortState> {