Skip to content

Commit

Permalink
fix: when update styles of node, it's react node does't update. (#6208)
Browse files Browse the repository at this point in the history
* feat: shape upsert support hook callback

* fix: fix g node doesnot update when node props change

* test: update test case

---------

Co-authored-by: antv <[email protected]>
  • Loading branch information
Aarebecca and antv authored Aug 22, 2024
1 parent 7cb9d26 commit b045322
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 16 deletions.
20 changes: 15 additions & 5 deletions packages/g6-extension-react/__tests__/demos/g-node.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { NodeData } from '@antv/g6';
import type { Graph as G6Graph, NodeData } from '@antv/g6';
import { ExtensionCategory, register } from '@antv/g6';
import { GNode, Group, Image, Rect, Text } from '../../src';
import { Graph } from '../../src/graph';
Expand All @@ -14,8 +14,9 @@ type Datum = {
failure: number;
};

const Node = ({ data, size }: { data: NodeData; size: [number, number] }) => {
const Node = ({ graph, data, size }: { graph: G6Graph; data: NodeData; size: [number, number] }) => {
const [width, height] = size;
const { lineWidth = 1 } = graph.getElementRenderStyle(data.id);

const { name, type, status, success, time, failure } = data.data as Datum;
const color = status === 'success' ? '#30BF78' : '#F4664A';
Expand Down Expand Up @@ -51,7 +52,14 @@ const Node = ({ data, size }: { data: NodeData; size: [number, number] }) => {

return (
<Group>
<Rect width={width} height={height} stroke={color} fill={'white'} radius={radius}>
<Rect
width={width}
height={height}
stroke={color}
fill={'white'}
radius={radius}
lineWidth={lineWidth ? lineWidth : 1}
>
<Rect width={width} height={20} fill={color} radius={[radius, radius, 0, 0]}>
<Image
src={
Expand Down Expand Up @@ -102,10 +110,12 @@ export const GNodeDemo = () => {
type: 'g',
style: {
size: [180, 60],
component: (data: NodeData) => <Node data={data} size={[180, 60]} />,
component: function (this: G6Graph, data: NodeData) {
return <Node graph={this} data={data} size={[180, 60]} />;
},
},
},
behaviors: ['drag-element', 'zoom-canvas', 'drag-canvas'],
behaviors: ['drag-element', 'zoom-canvas', 'drag-canvas', 'click-select'],
}}
/>
);
Expand Down
18 changes: 8 additions & 10 deletions packages/g6-extension-react/src/elements/g/node.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { DisplayObjectConfig } from '@antv/g';
import { ElementEvent, Group } from '@antv/g';
import { Group } from '@antv/g';
import type { BaseNodeStyleProps } from '@antv/g6';
import { Rect } from '@antv/g6';
import { render } from '@antv/react-g';
Expand Down Expand Up @@ -29,15 +29,13 @@ export class GNode extends Rect {
const { component } = attributes;
const [width, height] = this.getSize();

const dom = this.upsert('key', Group, { width, height }, container)!;

dom.isMutationObserved = true;
dom.addEventListener(ElementEvent.MOUNTED, () => {
// component 已经被回调机制自动创建为 ReactNode
// component has been automatically created as ReactNode by the callback mechanism
render(component as unknown as ReactNode, dom);
return this.upsert('key', Group, { width, height }, container, {
afterCreate: (dom) => {
render(component as unknown as ReactNode, dom);
},
afterUpdate: (dom) => {
render(component as unknown as ReactNode, dom);
},
});

return dom;
}
}
62 changes: 62 additions & 0 deletions packages/g6/__tests__/unit/elements/shape.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type { BaseShapeStyleProps } from '@/src';
import { BaseShape } from '@/src';
import { Circle } from '@antv/g';

describe('element shape', () => {
it('upsert hooks', () => {
interface ShapeStyleProps extends BaseShapeStyleProps {
shape: any;
}

const beforeCreate = jest.fn();
const afterCreate = jest.fn();
const beforeUpdate = jest.fn();
const afterUpdate = jest.fn();
const beforeDestroy = jest.fn();
const afterDestroy = jest.fn();

class Shape extends BaseShape<ShapeStyleProps> {
render() {
this.upsert('circle', Circle, this.attributes.shape, this, {
beforeCreate,
afterCreate,
beforeUpdate,
afterUpdate,
beforeDestroy,
afterDestroy,
});
}
}

const shape = new Shape({
style: {
shape: { r: 10 },
},
});

expect(beforeCreate).toHaveBeenCalledTimes(1);
expect(afterCreate).toHaveBeenCalledTimes(1);
expect(beforeUpdate).toHaveBeenCalledTimes(0);
expect(afterUpdate).toHaveBeenCalledTimes(0);
expect(beforeDestroy).toHaveBeenCalledTimes(0);
expect(afterDestroy).toHaveBeenCalledTimes(0);

shape.update({ shape: { r: 20 } });

expect(beforeCreate).toHaveBeenCalledTimes(1);
expect(afterCreate).toHaveBeenCalledTimes(1);
expect(beforeUpdate).toHaveBeenCalledTimes(1);
expect(afterUpdate).toHaveBeenCalledTimes(1);
expect(beforeDestroy).toHaveBeenCalledTimes(0);
expect(afterDestroy).toHaveBeenCalledTimes(0);

shape.update({ shape: false });

expect(beforeCreate).toHaveBeenCalledTimes(1);
expect(afterCreate).toHaveBeenCalledTimes(1);
expect(beforeUpdate).toHaveBeenCalledTimes(1);
expect(afterUpdate).toHaveBeenCalledTimes(1);
expect(beforeDestroy).toHaveBeenCalledTimes(1);
expect(afterDestroy).toHaveBeenCalledTimes(1);
});
});
63 changes: 62 additions & 1 deletion packages/g6/src/elements/shapes/base-shape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,37 +46,49 @@ export abstract class BaseShape<StyleProps extends BaseShapeStyleProps> extends
* @param Ctor - <zh> 图形类型 | <en> shape type
* @param style - <zh> 图形样式 | <en> shape style
* @param container - <zh> 容器 | <en> container
* @param hooks - <zh> 钩子函数 | <en> hooks
* @returns <zh> 图形实例 | <en> shape instance
*/
protected upsert<T extends DisplayObject>(
className: string,
Ctor: { new (...args: any[]): T },
style: T['attributes'] | false,
container: DisplayObject,
hooks?: UpsertHooks,
): T | undefined {
const target = this.shapeMap[className] as T | undefined;
// remove
// 如果 style 为 false,则删除图形 / remove shape if style is false
if (style === false) {
if (target) {
hooks?.beforeDestroy?.(target);
container.removeChild(target);
delete this.shapeMap[className];
hooks?.afterDestroy?.(target);
}
return;
}

// create
if (!target || target.destroyed || !(target instanceof Ctor)) {
target?.destroy();
if (target) {
hooks?.beforeDestroy?.(target);
target?.destroy();
hooks?.afterDestroy?.(target);
}

hooks?.beforeCreate?.();
const instance = new Ctor({ className, style });
container.appendChild(instance);
this.shapeMap[className] = instance;
hooks?.afterCreate?.(instance);
return instance;
}

// update
hooks?.beforeUpdate?.(target);
updateStyle(target, style);
hooks?.afterUpdate?.(target);

return target;
}
Expand Down Expand Up @@ -248,3 +260,52 @@ function releaseAnimation(target: DisplayObject, animation: IAnimation) {
if (index > -1) target.activeAnimations.splice(index, 1);
});
}

/**
* <zh/> 图形 upsert 方法生命周期钩子
*
* <en/> Shape upsert method lifecycle hooks
*/
export interface UpsertHooks {
/**
* <zh/> 图形创建前
*
* <en/> Before creating the shape
*/
beforeCreate?: () => void;
/**
* <zh/> 图形创建后
*
* <en/> After creating the shape
* @param instance - <zh/> 图形实例 | <en/> shape instance
*/
afterCreate?: (instance: DisplayObject) => void;
/**
* <zh/> 图形更新前
*
* <en/> Before updating the shape
* @param instance - <zh/> 图形实例 | <en/> shape instance
*/
beforeUpdate?: (instance: DisplayObject) => void;
/**
* <zh/> 图形更新后
*
* <en/> After updating the shape
* @param instance - <zh/> 图形实例 | <en/> shape instance
*/
afterUpdate?: (instance: DisplayObject) => void;
/**
* <zh/> 图形销毁前
*
* <en/> Before destroying the shape
* @param instance - <zh/> 图形实例 | <en/> shape instance
*/
beforeDestroy?: (instance: DisplayObject) => void;
/**
* <zh/> 图形销毁后
*
* <en/> After destroying the shape
* @param instance - <zh/> 图形实例 | <en/> shape instance
*/
afterDestroy?: (instance: DisplayObject) => void;
}
1 change: 1 addition & 0 deletions packages/g6/src/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export type {
LabelStyleProps,
PolygonStyleProps,
} from './elements/shapes';
export type { UpsertHooks } from './elements/shapes/base-shape';
export type { ContourLabelStyleProps, ContourStyleProps } from './elements/shapes/contour';
export type { BaseLayoutOptions, WebWorkerLayoutOptions } from './layouts/types';
export type { CategoricalPalette } from './palettes/types';
Expand Down

0 comments on commit b045322

Please sign in to comment.