1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00
lens/src/renderer/components/dock/upgrade-chart-store/upgrade-chart.store.ts
Janne Savolainen 4f5a2988cb
Fix infinite render loop in release details (#4710)
* Fix infinite render loop in release details by replacing stateful, UI-triggered releaseStore with reactive async computed

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Update injectable

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove unnecessary return

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove empty lines

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Allow injection of history

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make data required for opening of release details a dependency to make sure it's present

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove dead code

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make arriving release values not re-render whole details

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
2022-01-21 16:29:10 +02:00

129 lines
3.5 KiB
TypeScript

/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import {
action,
autorun,
computed,
IReactionDisposer,
reaction,
makeObservable,
} from "mobx";
import { DockStore, DockTab, TabId, TabKind } from "../dock-store/dock.store";
import { DockTabStorageState, DockTabStore } from "../dock-tab-store/dock-tab.store";
import {
getReleaseValues,
HelmRelease,
} from "../../../../common/k8s-api/endpoints/helm-releases.api";
import { iter, StorageHelper } from "../../../utils";
import type { IAsyncComputed } from "@ogre-tools/injectable-react";
export interface IChartUpgradeData {
releaseName: string;
releaseNamespace: string;
}
interface Dependencies {
releases: IAsyncComputed<HelmRelease[]>
valuesStore: DockTabStore<string>
dockStore: DockStore
createStorage: <T>(storageKey: string, options: DockTabStorageState<T>) => StorageHelper<DockTabStorageState<T>>
}
export class UpgradeChartStore extends DockTabStore<IChartUpgradeData> {
private watchers = new Map<string, IReactionDisposer>();
@computed private get releaseNameReverseLookup(): Map<string, string> {
return new Map(iter.map(this.data, ([id, { releaseName }]) => [releaseName, id]));
}
get values() {
return this.dependencies.valuesStore;
}
constructor(protected dependencies : Dependencies) {
super(dependencies, {
storageKey: "chart_releases",
});
makeObservable(this);
autorun(() => {
const { selectedTab, isOpen } = dependencies.dockStore;
if (selectedTab?.kind === TabKind.UPGRADE_CHART && isOpen) {
this.loadData(selectedTab.id);
}
}, { delay: 250 });
autorun(() => {
const objects = [...this.data.values()];
objects.forEach(({ releaseName }) => this.createReleaseWatcher(releaseName));
});
}
private createReleaseWatcher(releaseName: string) {
if (this.watchers.get(releaseName)) {
return;
}
const dispose = reaction(() => {
const release = this.dependencies.releases.value.get().find(release => release.getName() === releaseName);
return release?.getRevision(); // watch changes only by revision
},
release => {
const releaseTab = this.getTabByRelease(releaseName);
if (!releaseTab) {
return;
}
// auto-reload values if was loaded before
if (release) {
if (this.dependencies.dockStore.selectedTab === releaseTab && this.values.getData(releaseTab.id)) {
this.loadValues(releaseTab.id);
}
}
// clean up watcher, close tab if release not exists / was removed
else {
dispose();
this.watchers.delete(releaseName);
this.dependencies.dockStore.closeTab(releaseTab.id);
}
});
this.watchers.set(releaseName, dispose);
}
isLoading(tabId = this.dependencies.dockStore.selectedTabId) {
const values = this.values.getData(tabId);
return values === undefined;
}
@action
async loadData(tabId: TabId) {
const values = this.values.getData(tabId);
await Promise.all([
!values && this.loadValues(tabId),
]);
}
@action
async loadValues(tabId: TabId) {
this.values.clearData(tabId); // reset
const { releaseName, releaseNamespace } = this.getData(tabId);
const values = await getReleaseValues(releaseName, releaseNamespace, true);
this.values.setData(tabId, values);
}
getTabByRelease(releaseName: string): DockTab {
return this.dependencies.dockStore.getTabById(this.releaseNameReverseLookup.get(releaseName));
}
}