試作内容
<Child
Elements={el}
Styles={sy}
/>
上記のように、props で cytoscape へ設定するデータを渡して更新する Child コンポーネントの作成を目指します。
テンプレ作成
ベースとなるアプリは以下のコマンドで用意しました。
npx create-react-app my-app --template=typescript
cd my-app
yarn add cytoscape
yarn add -D @types/cytoscape
試作したコンポーネント
import React, { FC, useRef, useEffect } from 'react';
import cytoscape, { Core, ElementDefinition, Stylesheet } from 'cytoscape';
const Child: FC<{
Elements?: ElementDefinition[],
Styles?: Stylesheet[],
}> = ({
Elements = undefined,
Styles = undefined,
}) => {
const refEl = useRef(null);
const refCy = useRef<Core | null>(null);
useEffect(() => {
const container = refEl.current! as HTMLDivElement;
refCy.current = cytoscape({
container: container,
elements: Elements,
style: Styles,
});
const cy = refCy.current;
return (() => {
cy.destroy();
})
}, []);
useEffect(() => {
const cy = refCy.current;
cy?.json({ elements: Elements, styles: Styles });
}, [Elements, Styles]);
return (
<div ref={refEl} style={{ width: '300px', height: '200px' }}></div>
);
}
export default Child;
主なポイントは、useEffect を以下の2つに分けて処理するところでしょうか。
- 初期に描画する処理(14行目以降)
- 更新された props のデータで描画する処理(27行目以降)
14行目の useEffect の第2引数に Elements と Styles を入れても更新は可能でしたが、はじめからの描画になるので、図の変更(ノードの移動など)が戻ってしまいました。
その為、27行目の useEffect で props の更新に反応させ、
src/Child.tsx
Line 25:8: React Hook useEffect has missing dependencies: 'Elements' and 'Styles'.
Either include them or remove the dependency array react-hooks/exhaustive-deps
と言う指摘は無視しています。
やはりこの手のライブラリ使用は useEffect でハマりそうな気がしますね。
あと、cytoscape の要素の初期の高さをどうするか悩みそうでした。
何もしないと高さがゼロになって何も描かれないんですよね。
仕方がないので今回は固定値を設定しました。
作成したコンポーネントを使用したスクリーンショット
ボタンクリックで、Node1 の色が変わります。
- blank:黒
- A:赤
- B:青
その他のファイル
Child.tsx の他に、修正または追加したファイルは以下の通りです。
import React, { useState } from 'react';
import Child from './Child';
import Elements from './cy-element';
import Styles from './cy-style';
function App() {
const [el, setElements] = useState(Elements);
const [sy, setStyles] = useState(Styles);
const onClick = (type: string) => {
const node = el.find(e => e.data.id === "n01");
node!.data.type = type;
setElements([...el]);
};
return (
<div>
<Child
Elements={el}
Styles={sy}
/>
<button onClick={() => onClick('')}>blank</button>
<button onClick={() => onClick('A')}>A</button>
<button onClick={() => onClick('B')}>B</button>
</div>
);
}
export default App;
import { ElementDefinition } from 'cytoscape';
export default [{
"data": {
"id": "n01",
"label": "Node1",
"type": "A"
}
}, {
"data": {
"id": "n02",
"label": "Node2"
}
}, {
"data": {
"source": "n01",
"target": "n02"
}
}] as ElementDefinition[];
import { Stylesheet } from 'cytoscape';
export default
[
{
"selector": "node",
"style": {
'label': 'data(label)',
"background-color": "black"
}
},
{
"selector": "node[type='A']",
"style": {
"background-color": "red"
}
},
{
"selector": "node[type='B']",
"style": {
"background-color": "blue"
}
}
] as Stylesheet[];
コメント