Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/g6/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@antv/g6",
"version": "5.0.47",
"version": "5.0.48",
"description": "A Graph Visualization Framework in JavaScript",
"keywords": [
"antv",
Expand Down
18 changes: 18 additions & 0 deletions packages/g6/src/plugins/bubble-sets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ export class BubbleSets extends BasePlugin<BubbleSetsOptions> {

private bubbleSetOptions: IBubbleSetOptions = {};

private shallRunUpdateBubbleSetsPath = false;

private pendingRunUpdateBubbleSetsPath = false;

static defaultOptions: Partial<BubbleSetsOptions> = {
members: [],
avoidMembers: [],
Expand Down Expand Up @@ -125,6 +129,20 @@ export class BubbleSets extends BasePlugin<BubbleSetsOptions> {
};

private updateBubbleSetsPath = (event: ElementLifeCycleEvent) => {
if (!this.shallRunUpdateBubbleSetsPath) {
if (!this.pendingRunUpdateBubbleSetsPath) {
this.pendingRunUpdateBubbleSetsPath = true;
setTimeout(() => {
this.shallRunUpdateBubbleSetsPath = true;
this.updateBubbleSetsPath(event);
});
}
return;
}

this.pendingRunUpdateBubbleSetsPath = false;
this.shallRunUpdateBubbleSetsPath = false;

if (!this.shape) return;
const id = idOf(event.data);
if (![...this.options.members, ...this.options.avoidMembers].includes(id)) return;
Comment on lines 128 to 131
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The deferred execution logic is flawed and can lead to missed updates and memory leaks.

The event object from the first call is captured and used in the deferred execution. If the first call is for a non-member element, and a subsequent call for a member element happens before the timeout, the update for the member will be skipped. This is because the deferred check on line 146 will use the captured non-member event and fail.

The setTimeout is not cleared if the plugin is destroyed. This can lead to errors when the callback tries to access properties of a destroyed object.

Refactor this to a simpler and more robust debounce pattern. Check if the event is for a member element upfront, use a single timeout ID to manage the debounced call, and clear the timeout in a destroy() method.

  private updateBubbleSetsPath = (event: ElementLifeCycleEvent) => {
    if (!this.shape) return;
    const id = idOf(event.data);
    if (![...this.options.members, ...this.options.avoidMembers].includes(id)) return;

    if (this.updatePathTimeout) {
      clearTimeout(this.updatePathTimeout);
    }

    this.updatePathTimeout = setTimeout(() => {
      if (!this.shape) return;
      // The actual update logic, which was previously after the check, should be placed here.
      // For example: this.drawBubbleSets();
    }, 0);
  };

Expand Down
2 changes: 1 addition & 1 deletion packages/g6/src/version.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const version = '5.0.47';
export const version = '5.0.48';