Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

examples using hooks / useState ? #68

Open
dcsan opened this issue Jan 5, 2021 · 3 comments
Open

examples using hooks / useState ? #68

dcsan opened this issue Jan 5, 2021 · 3 comments

Comments

@dcsan
Copy link

dcsan commented Jan 5, 2021

I'm trying to create a functional component with this lib, and useState hooks, but having problems dealing with the cy instance.
I need to add events to the cy instance, eg tap. but this seems to create a memory leak - now each time the graph is rendered again, there's another 'graph' created. tapping will now send 3, 4, ... etc events.

what's the best way to manage this?
do i need to put some code in to try and remove the cy.on(...) event before the next render?

Thanks!

code is like this below

import React, { useState, useEffect } from 'react';

import Cytoscape from 'cytoscape';
import CytoscapeComponent from 'react-cytoscapejs'
import cola from 'cytoscape-cola';
// import cydagre from 'cytoscape-dagre';
import { graphStyle, nodeStyle } from './graphStyle'

const layout = {
  name: 'cola'
}
Cytoscape.use(cola);


// import { DcLib } from '../utils/DcLib'


const KGraph = (props: any) => {
  const graph = props?.graph
  const [cy, setCy] = useState({})

  useEffect(() => {
    console.log('effect')
    // cy.layout(layout).run()
  })

  console.log('graph', graph)

  const initCy = (cy: any) => {
    // @ts-ignore
    console.log('initCy')
    if (!cy) {
      setCy(cy)
    }

    cy.on('tap', (event: any) => {
      console.log('tap cy', event)
      console.log('target', event.target)
    });
  }


  if (!props.graph.ready) {
    return (<div> graph here </div>)
  }

  const layoutGraph = () => {
    // @ts-ignore
    cy.layout(layout).run()
  }

  return (
    <div>
      <button onClick={() => layoutGraph()}>redo graph</button>
      <CytoscapeComponent
        cy={initCy}
        elements={graph.elements}
        style={graphStyle}
        layout={layout}
        stylesheet={nodeStyle}
      />
    </div>
  )

}

export default KGraph
@williaster
Copy link

williaster commented Jan 12, 2021

I don't think I had this problem in my application using useRef with a couple of checks and cleanup.

function MyComponent() {
  const cyRef = useRef<CytoscapeRef | null>();

  // cleanup cytoscape listeners on unmount
  useEffect(() => {
    return () => {
      if (cyRef.current) {
        cyRef.current.removeAllListeners();
        cyRef.current = null;
      }
    };
  }, []);

  const cyCallback = useCallback(
    (cy: CytoscapeRef) => {
      // this is called each render of the component, don't add more listeners
      if (cyRef.current) return;

      cyRef.current = cy;
      cy.ready(...);
      cy.on(...);
    },
    [...dependencies],
  );

  return <Cytoscape cy={cyCallback} {...} />;
}

@dcsan
Copy link
Author

dcsan commented Jan 15, 2021

hmm ok useRef always feels a bit dirty to me like you're changing stuff that react wants to control, and it will bite you eventually...

@zirkelc
Copy link

zirkelc commented Feb 1, 2021

@williaster your code works well for me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants