JavaScript is required

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(), and setOptions().
  • Use RGHooks to 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 lineJson to 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.

11. Next Reading