Graph State Management (RGHooks / graphStoreMixin)
RGHooks is used to read relation-graph runtime reactive state. It is suitable for building toolbars, property panels, editing controllers, overview indicators, status bars, and other UI that should follow graph interactions.
Core principles:
- Modify graph data through instance APIs, such as
updateNode(),addLines(), andsetOptions(). - Use
RGHooksto read runtime state and drive UI rendering. - Most objects returned by hooks are runtime state. They are not recommended as persistent business data.
1. State Sources
relation-graph internally maintains:
RGOptionsFull: global options and runtime state.shouldRenderNodes,shouldRenderLines,shouldRenderFakeLines: the current data collections that should be rendered.- Editor state: node creation, line creation, node/line editing, selection box state, and similar interaction state.
React, Vue3, and Svelte read these states through RGHooks; Vue2 reads them through graphStoreMixin.
All components that use hooks or the mixin must be inside the same RGProvider or <RelationGraph> context. One important detail: in Vue/React/Svelte, a component’s script logic cannot consume the context that will be provided by a child <RelationGraph> rendered later in the same component template. Put hook-based toolbars, panels, and controllers into child components inside <RelationGraph> slots, or wrap the whole graph area with an outer RGProvider.
2. Hook List
| Hook / state | Returned content | Typical use |
|---|---|---|
useGraphInstance() |
RelationGraphInstance |
Call graph APIs for CRUD, layout, zooming, export, and similar operations. |
useCreatingLine() |
RGCreatingLine |
Show the line currently being dragged/created, or control connection hints. |
useCreatingNode() |
RGCreatingNode |
Show the node template currently being created or dragged in. |
useEditingNodes() |
RGEditingNodes |
Show node editing boxes, batch property panels, and resize controllers. |
useEditingLine() |
RGEditingLine |
Show line editors, path control points, and line text editors. |
useConnectingNode() |
RGConnectingNode |
Show connection controllers or anchor UI near a node. |
useViewInformation() |
RGViewInformation |
Show zoom, canvas offset, fullscreen state, and view size. |
useSelection() |
RGSelectionView & { show?: boolean } |
Show the selection rectangle or read the current selection box. |
useCheckedItem() |
RGCheckedItem |
Update toolbar availability according to the current checked node or line. |
useGraphOptions() |
Ref<RGOptionsFull> |
Currently exported on the Vue3 side for reading the complete options ref. React/Svelte usually read state through useGraphStore() or more specific hooks. |
useGraphStore() |
Framework-specific store/context | Advanced scenarios that need direct access to the underlying store. |
useAutoUpdateView() |
Auto refresh helper | Helper hook used by React for automatic view updates. |
Note: some older docs or examples may use useSelectionView(). The name exported by the current source code is useSelection(). If your installed package version additionally exports an alias, you can follow that version; for new code, prefer the actual current export.
3. React Usage
import {
RelationGraph,
RGHooks,
RGSlotOnView
} from '@relation-graph/react';
function GraphStatusBar() {
const graphInstance = RGHooks.useGraphInstance();
const checkedItem = RGHooks.useCheckedItem();
const view = RGHooks.useViewInformation();
const focusCheckedNode = () => {
if (checkedItem.checkedNodeId) {
graphInstance.focusNodeById(checkedItem.checkedNodeId);
}
};
return (
<div className="graph-status-bar">
<span>{view.canvasZoom}%</span>
<button disabled={!checkedItem.checkedNodeId} onClick={focusCheckedNode}>
Focus
</button>
</div>
);
}
function MyGraph() {
return (
<RelationGraph options={graphOptions}>
<RGSlotOnView>
<GraphStatusBar />
</RGSlotOnView>
</RelationGraph>
);
}
4. Vue3 Usage
The following example should be written in a child component under <RelationGraph>, for example a GraphPanel.vue component placed in the #view slot, or a child component wrapped by an outer RGProvider.
<script setup lang="ts">
import { RGHooks } from '@relation-graph/vue';
const graphInstance = RGHooks.useGraphInstance();
const checkedItem = RGHooks.useCheckedItem();
const viewInformation = RGHooks.useViewInformation();
const editingNodes = RGHooks.useEditingNodes();
const removeCheckedNode = () => {
if (checkedItem.value.checkedNodeId) {
graphInstance.removeNodeById(checkedItem.value.checkedNodeId);
}
};
</script>
<template>
<div class="graph-panel">
<span>{{ viewInformation.canvasZoom }}%</span>
<button
:disabled="!checkedItem.checkedNodeId"
@click="removeCheckedNode"
>
Delete Node
</button>
<span v-if="editingNodes.show">
Editing {{ editingNodes.nodes.length }} nodes
</span>
</div>
</template>
In the current source code, Vue3 useGraphInstance() directly returns the graph instance. State hooks such as useCheckedItem(), useViewInformation(), and useEditingNodes() return Ref or computed Ref. Therefore, read state with .value in <script setup>; templates usually unwrap refs automatically.
5. Svelte Usage
The current Svelte source exports:
useGraphInstance()useGraphStore()useCreatingLine()useCreatingNode()useEditingNodes()useEditingLine()useViewInformation()useSelection()useConnectingNode()useCheckedItem()
Example:
<script lang="ts">
import { RGHooks } from '@relation-graph/svelte';
const graphInstance = RGHooks.useGraphInstance();
const checkedItem = RGHooks.useCheckedItem();
const viewInformation = RGHooks.useViewInformation();
$: zoomText = `${$viewInformation.canvasZoom}%`;
function clearChecked() {
graphInstance.clearChecked();
}
</script>
<div class="graph-status">
<span>{zoomText}</span>
<button on:click={clearChecked} disabled={!$checkedItem.checkedNodeId && !$checkedItem.checkedLineId}>
Clear checked
</button>
</div>
6. Vue2: graphStoreMixin
Vue2 does not use Composition API hooks. The source provides graphStoreMixin, which lets child components under RGProvider or RelationGraph read graph state from this.
Readable fields:
| Field | Description |
|---|---|
this.graphInstance |
The graph instance. |
this.shouldRenderNodes |
The current rendered node collection. |
this.shouldRenderLines |
The current rendered normal line collection. |
this.shouldRenderFakeLines |
The current rendered fake line collection. |
this.creatingLine |
The line creation state. |
this.creatingNode |
The node creation state. |
this.editingNodes |
The node editing controller state. |
this.editingLine |
The line editing controller state. |
this.connectingNode |
The connecting node/target controller state. |
this.viewInformation |
The current view state. |
this.selectionView |
The current selection rectangle state, including show. |
this.checkedItem |
The current checked node id, checked line id, or dragging node id. |
Example:
import { graphStoreMixin } from '@relation-graph/vue2';
export default {
mixins: [graphStoreMixin],
computed: {
canDelete() {
return Boolean(this.checkedItem.checkedNodeId || this.checkedItem.checkedLineId);
}
},
methods: {
deleteChecked() {
if (this.checkedItem.checkedNodeId) {
this.graphInstance.removeNodeById(this.checkedItem.checkedNodeId);
}
if (this.checkedItem.checkedLineId) {
this.graphInstance.removeLineById(this.checkedItem.checkedLineId);
}
}
}
};
If the component is not placed inside the graph context, the mixin throws an error.
7. State Object Fields
RGCreatingLine
Indicates whether a line is currently being created.
type RGCreatingLine =
| {
creating: true;
fromTarget?: RGLineTarget;
toTarget?: RGLineTarget;
lineJson?: JsonLine;
}
| {
creating: false;
};
Field descriptions:
| Field | Description |
|---|---|
creating |
Whether the user is currently dragging or creating a line. |
fromTarget |
The current start target. It may be a node, connection point, or canvas point. |
toTarget |
The current end target. During dragging, it may be a temporary position. |
lineJson |
Template data for the line being created. |
Suitable for:
- Showing a “creating line” hint.
- Restricting UI operations based on the start/end target type.
- Customizing the preview shown during line creation.
Do not:
- Directly modify
lineJsonto create the final line. Complete creation through event callbacks or instance APIs.
RGCreatingNode
Indicates whether a node is currently being created.
type RGCreatingNode =
| {
creating: true;
nodeJson?: JsonNode;
}
| {
creating: false;
};
Field descriptions:
| Field | Description |
|---|---|
creating |
Whether a node is currently being created. |
nodeJson |
The current node template. |
This is suitable for UI that drags a node template onto the canvas.
RGEditingNodes
Represents the current node editing controller state.
type RGEditingNodes = {
show: boolean;
nodes: RGNode[];
x: number;
y: number;
width: number;
height: number;
};
Field descriptions:
| Field | Description |
|---|---|
show |
Whether the node editing controller is shown. |
nodes |
The collection of nodes currently being edited. |
x/y |
Position of the editing box in the view coordinate system. |
width/height |
Size of the editing box in the view coordinate system. |
Related APIs:
graphInstance.setEditingNodes([nodeA, nodeB]);
graphInstance.addEditingNode(nodeC);
graphInstance.removeEditingNode(nodeA);
graphInstance.toggleEditingNode(nodeB);
graphInstance.updateEditingControllerView();
RGEditingLine
Represents the current line editing controller state.
type RGEditingLine = {
show: boolean;
line: RGLine | null;
startPoint: RGPosition;
endPoint: RGPosition;
text: {
show: boolean;
x: number;
y: number;
width: number;
height: number;
};
ctrlPoints: RGPosition[];
selectedLines: string[];
line44Splits: RGCtrlPointForLine44[];
line49Points: RGPosition[];
ctrlPoint1: RGPosition;
ctrlPoint2: RGPosition;
toolbar: RGPosition;
};
Field descriptions:
| Field | Description |
|---|---|
show |
Whether the line editing controller is shown. |
line |
The line currently being edited. |
startPoint/endPoint |
Start/end positions of the line in the view. |
text |
Position and size of the line text editing area. |
ctrlPoints |
General control point collection. |
selectedLines |
IDs of multiple lines selected by the editor. |
line44Splits |
Segment control information for StandardOrthogonal lines. |
line49Points |
Control points for HardOrthogonal lines. |
ctrlPoint1/ctrlPoint2 |
Curve control points. |
toolbar |
Position of the line editing toolbar. |
This is advanced editor state. For ordinary display applications, reading show and line is usually enough.
RGConnectingNode
Represents the current node connection controller state.
type RGConnectingNode = {
show: boolean;
node: RGNode | RGLineTarget | RGRectTarget;
x: number;
y: number;
width: number;
height: number;
};
This is suitable for floating controllers near a node, such as “add relation” or “drag out a line”.
RGViewInformation
Represents current view and canvas transform state.
type RGViewInformation = {
viewSize: { width: number; height: number };
fullscreen: boolean;
canvasSize: { width: number; height: number };
canvasZoom: number;
canvasOffset: { x: number; y: number };
showEasyView?: boolean;
};
Field descriptions:
| Field | Description |
|---|---|
viewSize |
Graph viewport size. |
fullscreen |
Whether the graph is currently fullscreen. |
canvasSize |
Canvas size. |
canvasZoom |
Zoom percentage. 100 means 100%. |
canvasOffset |
Canvas offset within the view. |
showEasyView |
Whether simplified view is enabled in performance mode. |
Suitable for:
- Showing the zoom ratio in a status bar.
- Controlling disabled state of zoom buttons.
- Building custom minimaps or helper coordinate displays.
RGSelectionView
type RGSelectionView = {
x: number;
y: number;
width: number;
height: number;
};
The Vue2 mixin return value additionally includes show, which indicates whether the user is currently selecting.
x/y/width/height describe the selection rectangle in the view coordinate system. When you need canvas coordinates, use instance coordinate conversion APIs, or directly call:
const nodes = graphInstance.getNodesInSelectionView(selectionView);
RGCheckedItem
type RGCheckedItem = {
checkedLineId?: string;
checkedNodeId?: string;
draggingNodeId?: string;
};
Field descriptions:
| Field | Description |
|---|---|
checkedNodeId |
ID of the current checked node. |
checkedLineId |
ID of the current checked normal line or fake line. |
draggingNodeId |
ID of the node currently being dragged. |
Related APIs:
graphInstance.setCheckedNode('node-a');
graphInstance.setCheckedLine('line-a-b');
graphInstance.clearChecked();
8. Hooks vs. Instance APIs
The recommended pattern is to read state through hooks and change the graph through instance APIs. In React, hooks return plain objects:
const checkedItem = RGHooks.useCheckedItem();
const graphInstance = RGHooks.useGraphInstance();
function setNodeWarning() {
if (!checkedItem.checkedNodeId) return;
graphInstance.updateNode(checkedItem.checkedNodeId, {
color: '#fef3c7',
borderColor: '#f59e0b'
});
}
In Vue3 <script setup>, state hooks return Ref, so write:
const checkedItem = RGHooks.useCheckedItem();
const graphInstance = RGHooks.useGraphInstance();
function setNodeWarning() {
const nodeId = checkedItem.value.checkedNodeId;
if (!nodeId) return;
graphInstance.updateNode(nodeId, {
color: '#fef3c7',
borderColor: '#f59e0b'
});
}
Do not treat hook state as the only source of truth:
// Not recommended: directly mutate an object returned by hooks
checkedItem.checkedNodeId = 'node-a';
Correct approach:
graphInstance.setCheckedNode('node-a');
9. Common UI Scenarios
Toolbar Button Availability
React:
const checkedItem = RGHooks.useCheckedItem();
const canDelete = Boolean(
checkedItem.checkedNodeId || checkedItem.checkedLineId
);
Vue3 <script setup>:
import { computed } from 'vue';
const checkedItem = RGHooks.useCheckedItem();
const canDelete = computed(() => {
return Boolean(
checkedItem.value.checkedNodeId || checkedItem.value.checkedLineId
);
});
Node Property Panel
React:
const editingNodes = RGHooks.useEditingNodes();
if (editingNodes.show && editingNodes.nodes.length === 1) {
const node = editingNodes.nodes[0];
// Show single-node properties.
}
In Vue3 <script setup>, read editingNodes.value.show and editingNodes.value.nodes; in templates, you can write editingNodes.show directly.
Zoom Status Bar
React:
const view = RGHooks.useViewInformation();
const zoomText = `${view.canvasZoom}%`;
In Vue3 <script setup>, write view.value.canvasZoom; in templates, you can write view.canvasZoom directly.
Batch Editing After Selection
const onCanvasSelectionEnd = (selectionView) => {
const nodes = graphInstance.getNodesInSelectionView(selectionView);
graphInstance.setEditingNodes(nodes);
};
10. FAQ
Hook throws an error saying the graph instance cannot be found?
The component must be inside an RGProvider or <RelationGraph> context. In React, toolbars are usually placed inside RGSlotOnView; in Vue/Svelte, place them in the corresponding #view/slot scope.
Why did hook state change without syncing my business store?
Hooks represent graph runtime state. Your business store should be updated through events, instance API results, or your own synchronization logic. It is not recommended to persist hook state as business data directly.
Why is selectionView in view coordinates instead of canvas coordinates?
The selection box is drawn by dragging in the screen viewport, so it is naturally expressed in view coordinates. Use getNodesInSelectionView() when you need to match nodes.
Why are return value shapes not exactly the same across React, Vue, and Svelte?
Different frameworks use different reactivity mechanisms. React hooks usually return current values and trigger component re-rendering; Vue may return reactive objects or refs; Svelte commonly uses stores. Follow the types and examples exported by your current package version, but the field semantics are consistent.