Portal Community

BaseNode Abstract Class

// packages/flow-studio-designer/src/components/Nodes/Base/BaseNode.tsx
export abstract class BaseNode<P extends BaseNodeProps = BaseNodeProps>
  extends React.Component<P> {

  // Injected by withNodeHooks HOC
  protected store: WorkflowStore;
  protected designerMode: DesignerModeStore;
  protected bus: EventBus;

  // Abstract — subclasses must implement
  abstract renderBody(): ReactNode;

  // Template method — wraps renderBody() with the node-wrapper div
  render(): ReactNode {
    return (
      <div
        className="node-wrapper"
        data-node-id={this.props.id}
        data-selected={this.props.selected}
      >
        {this.renderBody()}
      </div>
    );
  }
}

withNodeHooks HOC

withNodeHooks is a Higher-Order Component that wraps any BaseNode subclass and injects the required store references via props. You must always wrap your renderer with it when registering:

// withNodeHooks injects store, designerMode, and bus into the class
export function withNodeHooks<T extends BaseNode>(
  NodeClass: new (props: any) => T
): React.FC<NodeProps> {
  return function WrappedNode(props: NodeProps) {
    const store = useWorkflowStore();
    const designerMode = useDesignerModeStore();
    const bus = useEventBus();
    return <NodeClass {...props} store={store} designerMode={designerMode} bus={bus} />;
  };
}

BaseNodeProps

// React Flow NodeProps + injected store refs
export interface BaseNodeProps extends NodeProps {
  store: WorkflowStore;
  designerMode: DesignerModeStore;
  bus: EventBus;
  // From React Flow NodeProps:
  // id, data, selected, dragging, xPos, yPos, type
}
Why a Class, Not a Function Component? BaseNode is a class component to support the withNodeHooks injection pattern cleanly. Function components in React Flow can have subtle re-render issues when combined with Zustand store subscriptions on the canvas. The class approach provides predictable lifecycle control.