plotly / react-cytoscapejs

React component for Cytoscape.js network visualisations
MIT License
483 stars 68 forks source link

No option to rerun layout after adding new elements #12

Closed alendit closed 5 years ago

alendit commented 5 years ago

Maybe I misunderstand it, but it seems to me that there is no way to rerun layout algorithms after when new elements get added/removed.

Is this something you'd welcome a PR for?

xhluca commented 5 years ago

It is possible to rerun it, using the cy handler. Here's what I'm using for Dash Cytoscape:

    handleCy(cy) {
        ...
        SELECT_THRESHOLD = 100;

        // Refresh Layout if needed
        const refreshLayout = _.debounce(() => {
            const {
                autoRefreshLayout,
                layout
            } = this.props;

            if (autoRefreshLayout) {
                cy.layout(layout).run()
            }
        }, SELECT_THRESHOLD);

        cy.on('add remove', () => {
            refreshLayout();
        });
    }

where autoRefreshLayout is a prop indicating whether to refresh the layout or not when a new element is added. The function handleCy() is used specifically to handle the cy, which is passed in the render() method, which would look something like that:

render() {
        const {
        ...
        } = this.props;

        return (
            <CytoscapeComponent
                cy={this.handleCy}
                elements={...}
                stylesheet={...}
            />
        )
    }
xhluca commented 5 years ago

Note here I'm using lodash for the debounce method. Please check out the official docs, or the react component for Dash Cytoscape

maxkfranz commented 5 years ago

This isn't possible to do declaratively, because each layout has a different set of options: The diff would be handled differently in each case. I suppose it could be supported for the simple === case, though that would have some caveats.

invisibleroads commented 5 years ago

@xhlulu Thank you sir! Your comment was a lifesaver.

erikhofer commented 2 years ago

Another option, if you don't want to use debounce, is using useEffect to re-run the layouts when elements change:

const elements = [ /* ... */ ]
const layout = { /* ... */ }

const [cy, setCy] = useState<Cytoscape.Core | null>(null)

useEffect(() => {
  cy?.layout(layout).run()
}, [cy, elements]) // make sure to add elements here

return <CytoscapeComponent cy={setCy} elements={elements} />