Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
92 changes: 60 additions & 32 deletions packages/site/common/react-snippet-strict.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,75 @@
```tsx
import type { GraphOptions } from '@antv/g6';
import { Graph as G6Graph } from '@antv/g6';
import { ExtensionCategory, Graph as G6Graph, register } from '@antv/g6';
import { ReactNode } from '@antv/g6-extension-react';
import { useEffect, useRef } from 'react';

export interface GraphProps {
options: GraphOptions;
onRender?: (graph: G6Graph) => void;
onDestroy?: () => void;
}
register(ExtensionCategory.NODE, 'react-node', ReactNode);

const SelectableNode = (props: { selected: boolean; onToggle: () => void }) => {
const { selected, onToggle } = props;

return (
<button
style={{
width: 160,
padding: 12,
border: `2px solid ${selected ? '#fa8c16' : '#d9d9d9'}`,
borderRadius: 8,
background: '#fff',
cursor: 'pointer',
}}
onClick={onToggle}
>
{selected ? 'Selected' : 'Click to select'}
</button>
);
};

export const Graph = (props: GraphProps) => {
const { options, onRender, onDestroy } = props;
const graphRef = useRef<G6Graph>();
export default function App() {
const containerRef = useRef<HTMLDivElement>(null);
const graphRef = useRef<G6Graph | null>(null);

useEffect(() => {
const graph = new G6Graph({ container: containerRef.current! });
graphRef.current = graph;
if (!containerRef.current) return;

return () => {
const handleToggle = (id: string, selected: boolean) => {
const graph = graphRef.current;
if (graph) {
graph.destroy();
onDestroy?.();
graphRef.current = undefined;
}
if (!graph) return;

graph.updateNodeData([{ id, data: { selected: !selected } }]);
graph.draw();
};
}, []);

useEffect(() => {
const container = containerRef.current;
const graph = graphRef.current;
const graph = new G6Graph({
container: containerRef.current,
width: 500,
height: 300,
data: {
nodes: [{ id: 'node-1', style: { x: 250, y: 150 }, data: { selected: false } }],
},
node: {
type: 'react-node',
style: {
size: [160, 50],
component: (data) => (
<SelectableNode
selected={Boolean(data.data?.selected)}
onToggle={() => handleToggle(data.id, Boolean(data.data?.selected))}
/>
),
},
},
});

if (!options || !container || !graph || graph.destroyed) return;
graphRef.current = graph;
graph.render();

graph.setOptions(options);
graph
.render()
.then(() => onRender?.(graph))
// eslint-disable-next-line no-console
.catch((error) => console.debug(error));
}, [options]);
return () => {
graph.destroy();
graphRef.current = null;
};
}, []);

return <div ref={containerRef} style={{ width: '100%', height: '100%' }} />;
};
return <div ref={containerRef} />;
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ Refer to the example below, you can use G6 in React, and you can also view the [

## Strict Mode

In strict mode, React will update twice, causing G6 to create and destroy the Graph instance repeatedly. You can refer to the following example to solve this problem:
In strict mode, React intentionally mounts, unmounts, and remounts components in development. Create the Graph instance inside an effect, keep it in a ref, and destroy it in the cleanup callback so the first development-only mount does not leave a stale graph behind. The following complete example also shows how to register and render a React node.

<embed src="@/common/react-snippet-strict"></embed>
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ order: 0

## 严格模式

在严格模式下,React 会二次更新导致 G6 重复创建 Graph 实例并销毁,可以参考如下示例解决:
在严格模式下,React 会在开发环境中有意执行挂载、卸载、再挂载。请把 Graph 实例放在 effect 里创建,用 ref 保存,并在清理函数中销毁,这样第一次开发态挂载不会留下旧实例。下面的完整示例同时演示了如何注册和渲染 React 节点。

<embed src="@/common/react-snippet-strict"></embed>