1
\$\begingroup\$

The React tutorial of joint js shows a way to use jointjs in react. The tutorial is quite limited: it is basically defining a special named div and putting all jointjs code inside.

The jointjs definition also looks quite different to standard react component definitions, really like an imperative instead of declarative solution, where I could do something along the lines of, to have a paper with a rectangle on it:

return <div>
    <Paper width={300} height={300} background={{ color: '#F5F5B5' }} cellViewNamespace={namespace}>
        <Rect x={100} y={150} width={80} height={40}/>
    </Paper>
</div>

I currently am working on two different approaches, both I fear are not ideal (so really open for advice on another).


One approach is to build the graph directly in the sub elements using useEffect, the sub elements are also responsible for removing themselves from the graph:

type PropTy = Omit<dia.Paper.Options, "el" | "model"> & {
    children?: ReactNode
    renderCount: number
}
export const Paper = (props: PropTy) => {
    const paperEl = useRef(null);
    const graph = useRef(new Graph({}, {cellNamespace: shapes}))

    const paper = useRef<dia.Paper|undefined>();

    const {
        children,
        width,
        height,
        background,
        cellViewNamespace,
    } = props;

    useEffect(() => {
        if (graph) {

            paper.current = new dia.Paper({
                el: paperEl.current,
                model: graph.current,

                width: width,
                height: height,
                background: background,
                cellViewNamespace:cellViewNamespace,
            });
        }


        return () => {
            if (paper.current) {
                paper.current.stopListening();
                paper.current = undefined;
            }
        };
    }, [
        graph,
        width,
        height,
        background,
        cellViewNamespace,
    ]);

    return (
        <GraphContext.Provider value={graph.current}>
            <div ref={paperEl} />
            {children}
        </GraphContext.Provider>
    );
}

And a shape:

export function Rect(props: RectProps) {
    const graph = useContext(GraphContext)
    const {x, y, width, height} = props;

    useEffect(() => {
        const rect = new shapes.standard.Rectangle();
        rect.position(x, y);
        rect.resize(width, height);
        rect.addTo(graph);
        return () => {
            rect.remove();
        }
    }, [graph, x, y, width, height]);

    return <></>;
}

The other approach is to rebuild the shapes on every render of the "paper", so I don't have to think about removing them, and the sub elements do not use useEffect:

type PropTy = Omit<dia.Paper.Options, "el" | "model"> & {
    children?: ReactNode
    renderCount: number
}
export const Paper = (props: PropTy) => {
    const paperEl = useRef(null);
    const graph = useRef(new Graph({}, {cellNamespace: shapes}))

    const paper = useRef<dia.Paper|undefined>();
    const elements: dia.Element[] = []

    const {
        children,
        width,
        height,
        background,
        cellViewNamespace,
    } = props;

    useEffect(() => {
        if (graph) {

            paper.current = new dia.Paper({
                el: paperEl.current,
                model: graph.current,

                width: width,
                height: height,
                background: background,
                cellViewNamespace:cellViewNamespace,
            });
        }


        return () => {
            if (paper.current) {
                paper.current.stopListening();
                paper.current = undefined;
            }
        };
    }, [
        graph,
        width,
        height,
        background,
        cellViewNamespace,
    ]);

    useLayoutEffect(() => {
        console.log("use layout effect of paper " + renderCount)
        graph.current.clear()
        for (const elem of elements) {
            elem.addTo(graph.current);
        }
    });
    return (
        <GraphContext.Provider value={graph.current}>
            <div ref={paperEl} />
            {children}
        </GraphContext.Provider>
    );
}

And a shape:

export function Rect(props: RectProps) {
    const elements = useContext(ElementContext);
    const {x, y, width, height} = props;
    const rect = new shapes.standard.Rectangle();
    rect.position(x, y);
    rect.resize(width, height);
    // need to test if rect goes into a useMemo...
    elements.push(rect);

    return <></>
}

I am a bit confused which would be best: mostly because the react documentation seems to stress not to use useEffect or useLayoutEffect.

\$\endgroup\$

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.