projectstorm / react-diagrams

a super simple, no-nonsense diagramming library written in react that just works
https://projectstorm.cloud/react-diagrams
MIT License
8.65k stars 1.17k forks source link

Basic use of node and port factories results in infinite render loop #849

Closed lerickson-control4 closed 3 years ago

lerickson-control4 commented 3 years ago

Hi. I really like what I see in react-diagrams, but I've run into an issue that I can't noodle my way out of. I have run some of the more basic samples successfully, and then started experimenting with node and port factories and everything blew up (read: I'm stuck in an infinite render loop). I can't tell what state change is triggering the renders. I'm following (sort of) the demo-custom-node1 sample.

Some of the pertinent details:

App.ts

function App() {
  const engine = initEngine();

  return (
    <div className="container" style={{height: '100vh', width: '100vw'}}>
      <CanvasWidget engine={engine} className="canvas" />
    </div>
  );
}

initEngine.ts

const initEngine = () => {

    console.log('initEngine');
    var engine = createEngine();

    const factory = new BoundNodeFactory();
    engine.getNodeFactories().registerFactory(factory);

    var model = new DiagramModel();

    const nodes = getItems().map((item: Item, index: number) => buildNode(item, index));
    model.addAll(...nodes);

    engine.setModel(model);

    return engine;
}

export default initEngine;

BoundNodeFactory.tsx (excerpt)

  generateReactWidget(event: GenerateWidgetEvent<BoundNodeModel>): JSX.Element {
    console.log('generating widget');
    return <BoundNode engine={this.engine} size={50} node={event.model} />;
  }

  generateModel(event: GenerateModelEvent) {
    console.log('generating model');
    return new BoundNodeModel(event.initialConfig);
  }

BoundNodeModel.ts

export interface BoundNodeModelOptions extends DefaultNodeModelOptions {
    item?: Item;
}

export interface BoundNodeModelGenerics extends DefaultNodeModelGenerics {
    OPTIONS: BoundNodeModelOptions;
}

export class BoundNodeModel extends NodeModel<BoundNodeModelGenerics> {
    public item?: Item;

    constructor(options: BoundNodeModelOptions) {
        super(options);
        this.item = options.item;
    }
}

BoundNode.tsx

export default function BoundNode(props: BoundNodeProps) {
  console.log(props);
  const {item} = props.node;
  return (
    <NodeWidget node={props.node} diagramEngine={props.engine}>
      <div className={`node ${item?.type}`}>
        <div className="header">
          <div className="name">{item?.name}</div>
          <div className="location"></div>
        </div>
        <div className="ports">
          <div className="inputs"></div>
          <div className="outputs">
            <div className="events"></div>
            <div className="varaibles"></div>
          </div>
        </div>
      </div>
    </NodeWidget>
  );
}

What I see in the console output is one logging of "initEngine", one logging of "generating model", but endless logging of "generating widget", meaning the factory's generateReactWidget method is getting spammed, meaning GenerateWidgetEvents are getting spammed, and I don't know why. The BoundNode props are also logged endlessly.

Any chance I could get another pair of eyes on this? It seems like I'm following "the pattern" precisely.

My repo is found here: https://github.com/lerickson-control4/react-diagrams-test

lerickson-control4 commented 3 years ago

for completeness ,buildNode.ts looks like this:

export default function buildNode(item: Item, index: number) {
    const node = new BoundNodeModel({
        item,
        name: item.name,
        color: item.type === "agent" ? "darkorchid" : "darkorange",
        type: "boundNode"
    });
    node.setPosition(randomInteger(1024), randomInteger(768));

    return node;
}
rob-myers commented 3 years ago

I don't think you need the <NodeWidget> in BoundNode.tsx. You could just use a div.

See the custom node example here:

https://github.com/projectstorm/react-diagrams/blob/master/diagrams-demo-project/src/custom-node-ts/TSCustomNodeWidget.tsx

lerickson-control4 commented 3 years ago

@rob-myers sure enough, removing NodeWidget did the trick. Thanks!