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, queryPath: msg.queryPath,
}); });
void this.loadResults();
break; break;
case "showInterpretedPage": { case "showInterpretedPage": {
const tableName = const tableName =
@@ -141,7 +140,6 @@ export class ResultsApp extends React.Component<
queryName: msg.queryName, queryName: msg.queryName,
queryPath: msg.queryPath, queryPath: msg.queryPath,
}); });
void this.loadResults();
break; break;
} }
case "resultsUpdating": case "resultsUpdating":
@@ -164,31 +162,61 @@ export class ResultsApp extends React.Component<
private updateStateWithNewResultsInfo(resultsInfo: ResultsInfo): void { private updateStateWithNewResultsInfo(resultsInfo: ResultsInfo): void {
this.setState((prevState) => { this.setState((prevState) => {
const stateWithDisplayedResults = ( if (resultsInfo === null) {
displayedResults: ResultsState, if (prevState.isExpectingResultsUpdate) {
): ResultsViewState => ({ // Display loading message
displayedResults, return {
isExpectingResultsUpdate: prevState.isExpectingResultsUpdate, displayedResults: {
nextResultsInfo: resultsInfo, 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,
};
}
}
if (!prevState.isExpectingResultsUpdate && resultsInfo === null) { let results: Results | null = null;
// No results to display let statusText = "";
return stateWithDisplayedResults({ try {
resultsInfo: null, const resultSets = this.getResultSets(resultsInfo);
results: null, results = {
errorMessage: "No results to display", resultSets,
}); database: resultsInfo.database,
sortStates: this.getSortStates(resultsInfo),
};
} catch (e) {
let errorMessage: string;
if (e instanceof Error) {
errorMessage = e.message;
} else {
errorMessage = "Unknown error";
}
statusText = `Error loading results: ${errorMessage}`;
} }
if (!resultsInfo || !resultsInfo.shouldKeepOldResultsWhileRendering) {
// Display loading message return {
return stateWithDisplayedResults({ displayedResults: {
resultsInfo: null, resultsInfo,
results: null, results,
errorMessage: "Loading results…", errorMessage: statusText,
}); },
} nextResultsInfo: null,
return stateWithDisplayedResults(prevState.displayedResults); isExpectingResultsUpdate: false,
};
}); });
} }
@@ -203,49 +231,6 @@ export class ResultsApp extends React.Component<
return [resultSet]; return [resultSet];
} }
private async loadResults(): Promise<void> {
const resultsInfo = this.state.nextResultsInfo;
if (resultsInfo === null) {
return;
}
let results: Results | null = null;
let statusText = "";
try {
const resultSets = this.getResultSets(resultsInfo);
results = {
resultSets,
database: resultsInfo.database,
sortStates: this.getSortStates(resultsInfo),
};
} catch (e) {
let errorMessage: string;
if (e instanceof Error) {
errorMessage = e.message;
} else {
errorMessage = "Unknown error";
}
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,
results,
errorMessage: statusText,
},
nextResultsInfo: null,
isExpectingResultsUpdate: false,
};
});
}
private getSortStates( private getSortStates(
resultsInfo: ResultsInfo, resultsInfo: ResultsInfo,
): Map<string, RawResultsSortState> { ): Map<string, RawResultsSortState> {