Commit a633cc77 authored by Laurent Wouters's avatar Laurent Wouters
Browse files

[DefaultEntityForm] Fix issue with concurrent load operations with cancel mechanism

--HG--
branch : v0.6.x
parent 3e8161f4c58c
Pipeline #105257 failed with stages
in 4 minutes and 51 seconds
......@@ -26,16 +26,17 @@ export class DefaultEntityForm extends EntityForm<
public static defaultProps = ENTITY_DEFAULT_PROPS;
state: EntityFormState;
_isMounted: boolean;
_currentLoadId: number;
constructor(props: Readonly<EntityFormProps>) {
super(props);
this._isMounted = false;
this._currentLoadId = -1;
this.state = {
statuts: EntityStatus.loading,
data: null,
schema: null,
} as any;
this.loadData = this.loadData.bind(this);
}
componentDidMount() {
......@@ -67,50 +68,69 @@ export class DefaultEntityForm extends EntityForm<
/**
* Loads the entity data and schema
*/
loadData(): void {
let self = this;
loadData = () => {
// increment load id and begins loading with this identifier
// this is safe because of the javascript's GIL
this._currentLoadId += 1;
this.doLoad(this._currentLoadId);
};
/**
* Checks whether the current load operation must stop
* A load operation may take time and thus it may become
* dangerous, or superseded by another one in the following cases:
* - the component has been unmounted, we cannot update the state anymore
* - this is no longer the latest load operation, another one has been launched
* @param loadId The identifier of the load operation
* @returns true if the load operation must stop
*/
stopLoadOp = (loadId: number) => {
return !this._isMounted || this._currentLoadId !== loadId;
};
/**
* Executes a load operation with the specified identifier
* @param loadId The identifier of this load operation
*/
doLoad = async (loadId: number) => {
this.setState({
status: EntityStatus.loading,
error: null,
});
this.context.schemaProvider
.getEntitySchemaFor(this.props.eid)
.then((schema: providers.EntitySchema | null) => {
if (!self._isMounted) return null;
this.setState({
status:
schema !== null
? EntityStatus.loading
: EntityStatus.nodata,
schema: schema,
});
if (schema === null) {
return null;
}
return this.context.entityProvider
.getEntityGivenSchema(this.props.eid, schema)
.then((data: providers.Entity | null) => {
if (self._isMounted) {
this.setState({
status:
data !== null
? EntityStatus.withdata
: EntityStatus.nodata,
data: data,
});
}
return data;
});
})
.catch((reason: any) => {
this.setState({
status: EntityStatus.onerror,
error: reason,
data: null,
schema: null,
});
try {
const schema = await this.context.schemaProvider.getEntitySchemaFor(
this.props.eid
);
if (this.stopLoadOp(loadId)) return;
this.setState({
status:
schema !== null
? EntityStatus.loading
: EntityStatus.nodata,
schema: schema,
});
}
if (schema === null) {
return;
}
const data = await this.context.entityProvider.getEntityGivenSchema(
this.props.eid,
schema
);
if (this.stopLoadOp(loadId)) return;
this.setState({
status:
data !== null ? EntityStatus.withdata : EntityStatus.nodata,
data: data,
});
} catch (reason) {
this.setState({
status: EntityStatus.onerror,
error: reason,
data: null,
schema: null,
});
}
};
render(): JSX.Element {
return this.props.render(this);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment