Skip to content

Commit

Permalink
fix: fix react node destruction logic (#5787)
Browse files Browse the repository at this point in the history
* fix: fix unexpect drag-canvas update

* refactor(elements): define base-element

* fix: fix html and react node destroy logic

* test: update test case
  • Loading branch information
Aarebecca authored May 30, 2024
1 parent 320c063 commit 04239f8
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 126 deletions.
99 changes: 73 additions & 26 deletions packages/g6-extension-react/__tests__/demos/react-node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,32 +120,79 @@ export const ReactNodeDemo = () => {
/>
</Form.Item>
<Form.Item>
<Button
style={{ width: '100%' }}
type="primary"
onClick={() => {
if (!graphRef.current || graphRef.current.destroyed) return;
const type = form.getFieldValue('serverType');
const status = 'warning';
const length = (options.data?.nodes || []).filter((node) => node?.data?.type === type).length;
setOptions((prev) => ({
...prev,
data: {
...prev.data,
nodes: [
...graphRef.current!.getNodeData(),
{
id: `${type}-server-${length + 1}`,
data: { type, status },
style: { x: type === 'local' ? 50 : 350, y: 50 + length * 120 },
},
],
},
}));
}}
>
Add Node
</Button>
<Button.Group>
<Button
style={{ width: '100%' }}
type="primary"
onClick={() => {
if (!graphRef.current || graphRef.current.destroyed) return;
const type = form.getFieldValue('serverType');
const status = 'warning';
const length = (options.data?.nodes || []).filter((node) => node?.data?.type === type).length;
setOptions((options) => ({
...options,
data: {
...options.data,
nodes: [
...graphRef.current!.getNodeData(),
{
id: `${type}-server-${length + 1}`,
data: { type, status },
style: { x: type === 'local' ? 50 : 350, y: 50 + length * 120 },
},
],
},
}));
}}
>
Add Node
</Button>
<Button
onClick={() => {
const { data } = options;
const nodes = data?.nodes || [];
setOptions((options) => ({
...options,
data: {
...options.data,
nodes: nodes.map((node, index) => {
// return node;
if (index === nodes.length - 1) {
return {
...node,
data: {
...node.data,
status: node.data!.status === 'success' ? 'warning' : 'success',
},
};
}
return node;
}),
},
}));

graphRef.current?.draw();
}}
>
Update Node
</Button>
<Button
danger
onClick={() => {
const { data } = options;
const nodes = data?.nodes || [];
setOptions((options) => ({
...options,
data: {
...options.data,
nodes: nodes.filter((node, index) => index !== nodes.length - 1),
},
}));
}}
>
Remove Node
</Button>
</Button.Group>
</Form.Item>
</Form>

Expand Down
4 changes: 2 additions & 2 deletions packages/g6-extension-react/src/elements/react/node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export class ReactNode extends HTML {
}
}

public onDestroy() {
public destroy(): void {
this.root.unmount();
super.onDestroy();
super.destroy();
}
}
7 changes: 6 additions & 1 deletion packages/g6/src/behaviors/drag-canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,13 @@ export class DragCanvas extends BaseBehavior<DragCanvasOptions> {
* @internal
*/
public update(options: Partial<DragCanvasOptions>): void {
this.unbindEvents();
super.update(options);
this.bindEvents();
}

private bindEvents() {
const { trigger } = this.options;
this.shortcut.unbindAll();
const { graph } = this.context;

if (isObject(trigger)) {
Expand Down Expand Up @@ -144,6 +144,11 @@ export class DragCanvas extends BaseBehavior<DragCanvasOptions> {
return !!enable;
}

private unbindEvents() {
this.shortcut.unbindAll();
this.context.graph.off(CanvasEvent.DRAG, this.onDrag);
}

public destroy(): void {
this.shortcut.destroy();
this.context.canvas.setCursor(this.defaultCursor);
Expand Down
52 changes: 52 additions & 0 deletions packages/g6/src/elements/base-element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { IAnimation } from '@antv/g';
import { Keyframe } from '../types';
import type { BaseShapeStyleProps } from './shapes';
import { BaseShape } from './shapes';

export abstract class BaseElement<T extends BaseShapeStyleProps> extends BaseShape<T> {
public get parsedAttributes() {
return this.attributes as Required<T>;
}

/**
* <zh/> 动画帧执行函数
*
* <en/> Animation frame execution function
*/
protected onframe() {}

public animate(keyframes: Keyframe[], options?: number | KeyframeAnimationOptions | undefined): IAnimation | null {
const animation = super.animate(keyframes, options);

if (animation) {
animation.onframe = () => this.onframe();
animation.finished.then(() => this.onframe());
}

return animation;
}

/**
* <zh/> 在元素完成创建并执行完入场动画后调用
*
* <en/> Called after the element is created and the entrance animation is completed
* @override
*/
public onCreate() {}

/**
* <zh/> 在元素更新并执行完过渡动画后调用
*
* <en/> Called after the element is updated and the transition animation is completed
* @override
*/
public onUpdate() {}

/**
* <zh/> 在元素完成退场动画并销毁后调用
*
* <en/> Called after the element completes the exit animation and is destroyed
* @override
*/
public onDestroy() {}
}
24 changes: 0 additions & 24 deletions packages/g6/src/elements/combos/base-combo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,30 +227,6 @@ export abstract class BaseCombo<S extends BaseComboStyleProps = BaseComboStylePr
this.drawCollapsedMarkerShape(attributes, container);
}

/**
* <zh/> 在元素完成创建并执行完入场动画后调用
*
* <en/> Called after the element is created and the entrance animation is completed
* @override
*/
public onCreate() {}

/**
* <zh/> 在元素更新并执行完过渡动画后调用
*
* <en/> Called after the element is updated and the transition animation is completed
* @override
*/
public onUpdate() {}

/**
* <zh/> 在元素完成退场动画并销毁后调用
*
* <en/> Called after the element completes the exit animation and is destroyed
* @override
*/
public onDestroy() {}

protected hostingAnimation = false;

protected onframe() {
Expand Down
32 changes: 2 additions & 30 deletions packages/g6/src/elements/edges/base-edge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ import { omitStyleProps, subStyleProps } from '../../utils/prefix';
import { parseSize } from '../../utils/size';
import * as Symbol from '../../utils/symbol';
import { getWordWrapWidthByEnds } from '../../utils/text';
import { BaseElement } from '../base-element';
import type { LabelStyleProps } from '../shapes';
import { Label } from '../shapes';
import { BaseShape } from '../shapes/base-shape';

/**
* <zh/> 边的通用样式属性
Expand Down Expand Up @@ -167,7 +167,7 @@ type ParsedBaseEdgeStyleProps = Required<BaseEdgeStyleProps>;
*
* <en/> Base class of the edge
*/
export abstract class BaseEdge extends BaseShape<BaseEdgeStyleProps> {
export abstract class BaseEdge extends BaseElement<BaseEdgeStyleProps> {
public type = 'edge';

static defaultStyleProps: Partial<BaseEdgeStyleProps> = {
Expand Down Expand Up @@ -365,30 +365,6 @@ export abstract class BaseEdge extends BaseShape<BaseEdgeStyleProps> {
this.drawHaloShape(attributes, container);
}

/**
* <zh/> 在元素完成创建并执行完入场动画后调用
*
* <en/> Called after the element is created and the entrance animation is completed
* @override
*/
public onCreate() {}

/**
* <zh/> 在元素更新并执行完过渡动画后调用
*
* <en/> Called after the element is updated and the transition animation is completed
* @override
*/
public onUpdate() {}

/**
* <zh/> 在元素完成退场动画并销毁后调用
*
* <en/> Called after the element completes the exit animation and is destroyed
* @override
*/
public onDestroy() {}

protected onframe() {
this.drawKeyShape(this.parsedAttributes, this);
this.drawHaloShape(this.parsedAttributes, this);
Expand All @@ -398,10 +374,6 @@ export abstract class BaseEdge extends BaseShape<BaseEdgeStyleProps> {
public animate(keyframes: Keyframe[], options?: number | KeyframeAnimationOptions) {
const animation = super.animate(keyframes, options);

if (animation) {
animation.onframe = () => this.onframe();
}

if (!animation) return animation;

// 设置 currentTime 时触发更新
Expand Down
45 changes: 3 additions & 42 deletions packages/g6/src/elements/nodes/base-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type { NodeData } from '../../spec';
import type {
BaseElementStyleProps,
ID,
Keyframe,
NodeBadgeStyleProps,
NodeLabelStyleProps,
NodePortStyleProps,
Expand All @@ -25,8 +24,9 @@ import { omitStyleProps, subObject, subStyleProps } from '../../utils/prefix';
import { parseSize } from '../../utils/size';
import { getWordWrapWidthByBox } from '../../utils/text';
import { replaceTranslateInTransform } from '../../utils/transform';
import { BaseElement } from '../base-element';
import type { BadgeStyleProps, IconStyleProps, LabelStyleProps } from '../shapes';
import { Badge, BaseShape, Icon, Label } from '../shapes';
import { Badge, Icon, Label } from '../shapes';

/**
* <zh/> 节点通用样式配置项
Expand Down Expand Up @@ -184,7 +184,7 @@ export interface BaseNodeStyleProps
*
* <en/> Design document: https://www.yuque.com/antv/g6/gl1iof1xpzg6ed98
*/
export abstract class BaseNode<S extends BaseNodeStyleProps = BaseNodeStyleProps> extends BaseShape<S> {
export abstract class BaseNode<S extends BaseNodeStyleProps = BaseNodeStyleProps> extends BaseElement<S> {
public type = 'node';

static defaultStyleProps: Partial<BaseNodeStyleProps> = {
Expand Down Expand Up @@ -221,10 +221,6 @@ export abstract class BaseNode<S extends BaseNodeStyleProps = BaseNodeStyleProps
super(deepMix({}, { style: BaseNode.defaultStyleProps }, options));
}

public get parsedAttributes() {
return this.attributes as Required<S>;
}

protected getSize(attributes = this.attributes) {
const { size } = attributes;
return parseSize(size);
Expand Down Expand Up @@ -430,43 +426,8 @@ export abstract class BaseNode<S extends BaseNodeStyleProps = BaseNodeStyleProps
this.drawPortShapes(attributes, container);
}

/**
* <zh/> 在元素完成创建并执行完入场动画后调用
*
* <en/> Called after the element is created and the entrance animation is completed
* @override
*/
public onCreate() {}

/**
* <zh/> 在元素更新并执行完过渡动画后调用
*
* <en/> Called after the element is updated and the transition animation is completed
* @override
*/
public onUpdate() {}

/**
* <zh/> 在元素完成退场动画并销毁后调用
*
* <en/> Called after the element completes the exit animation and is destroyed
* @override
*/
public onDestroy() {}

protected onframe() {
this.drawBadgeShapes(this.parsedAttributes, this);
this.drawLabelShape(this.parsedAttributes, this);
}

public animate(keyframes: Keyframe[], options?: number | KeyframeAnimationOptions) {
const animation = super.animate(keyframes, options);

if (animation) {
animation.onframe = () => this.onframe();
animation.finished.then(() => this.onframe());
}

return animation;
}
}
10 changes: 9 additions & 1 deletion packages/g6/src/elements/nodes/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,13 @@ export class HTML extends BaseNode<HTMLStyleProps> {
}
}

public onDestroy() {
public destroy() {
const element = this.getDomElement();
this.events.forEach((eventName) => {
// @ts-expect-error assert event is PointerEvent
element.removeEventListener(eventName, this.forwardEvents);
});
super.destroy();
}

private forwardEvents = (nativeEvent: PointerEvent) => {
Expand Down Expand Up @@ -245,4 +246,11 @@ export class HTML extends BaseNode<HTMLStyleProps> {
}
return { x, y };
}

protected onframe(): void {
super.onframe();
// sync opacity
const { opacity } = this.attributes;
this.getDomElement().style.opacity = `${opacity}`;
}
}

0 comments on commit 04239f8

Please sign in to comment.