ソースを参照

performance improvements & refactor

AykutSarac 2 年 前
コミット
7f8bd9d1f7

+ 0 - 0
src/components/ErrorContainer/ErrorContainer.tsx → src/components/ErrorContainer/index.tsx


+ 4 - 65
src/components/Graph/index.tsx

@@ -6,12 +6,9 @@ import {
 } from "react-zoom-pan-pinch";
 import { Canvas, Edge, ElkRoot } from "reaflow";
 import { CustomNode } from "src/components/CustomNode";
-import { NodeModal } from "src/containers/Modals/NodeModal";
 import useConfig from "src/hooks/store/useConfig";
-import styled from "styled-components";
-import shallow from "zustand/shallow";
 import useGraph from "src/hooks/store/useGraph";
-import { parser } from "src/utils/jsonParser";
+import styled from "styled-components";
 
 interface LayoutProps {
   isWidget: boolean;
@@ -41,27 +38,21 @@ const StyledEditorWrapper = styled.div<{ isWidget: boolean }>`
   }
 `;
 
-const MemoizedGraph = React.memo(function Layout({
+export const Graph = ({
   isWidget,
   openModal,
   setSelectedNode,
-}: LayoutProps) {
-  const json = useConfig((state) => state.json);
+}: LayoutProps) => {
   const setConfig = useConfig((state) => state.setConfig);
+  const layout = useConfig((state) => state.layout);
   const nodes = useGraph((state) => state.nodes);
   const edges = useGraph((state) => state.edges);
-  const setGraphValue = useGraph((state) => state.setGraphValue);
 
   const [size, setSize] = React.useState({
     width: 2000,
     height: 2000,
   });
 
-  const [expand, layout] = useConfig(
-    (state) => [state.expand, state.layout],
-    shallow
-  );
-
   const handleNodeClick = React.useCallback(
     (e: React.MouseEvent<SVGElement>, data: NodeData) => {
       setSelectedNode(data.text);
@@ -88,13 +79,6 @@ const MemoizedGraph = React.memo(function Layout({
     if (input) input.blur();
   }, []);
 
-  React.useEffect(() => {
-    const { nodes, edges } = parser(json, expand);
-
-    setGraphValue("nodes", nodes);
-    setGraphValue("edges", edges);
-  }, [expand, json, setGraphValue]);
-
   return (
     <StyledEditorWrapper isWidget={isWidget}>
       <TransformWrapper
@@ -145,49 +129,4 @@ const MemoizedGraph = React.memo(function Layout({
       </TransformWrapper>
     </StyledEditorWrapper>
   );
-});
-
-export const Graph = ({ isWidget = false }: { isWidget?: boolean }) => {
-  const [isModalVisible, setModalVisible] = React.useState(false);
-  const [selectedNode, setSelectedNode] = React.useState<[string, string][]>(
-    []
-  );
-
-  const openModal = React.useCallback(() => setModalVisible(true), []);
-
-  const collapsedNodes = useGraph((state) => state.collapsedNodes);
-  const collapsedEdges = useGraph((state) => state.collapsedEdges);
-
-  React.useEffect(() => {
-    const nodeList = collapsedNodes.map((id) => `[id$="node-${id}"]`);
-    const edgeList = collapsedEdges.map((id) => `[class$="edge-${id}"]`);
-
-    const hiddenItems = document.querySelectorAll(".hide");
-    hiddenItems.forEach((item) => item.classList.remove("hide"));
-
-    if (nodeList.length) {
-      const selectedNodes = document.querySelectorAll(nodeList.join(","));
-      const selectedEdges = document.querySelectorAll(edgeList.join(","));
-
-      selectedNodes.forEach((node) => node.classList.add("hide"));
-      selectedEdges.forEach((edge) => edge.classList.add("hide"));
-    }
-  }, [collapsedNodes, collapsedEdges]);
-
-  return (
-    <>
-      <MemoizedGraph
-        openModal={openModal}
-        setSelectedNode={setSelectedNode}
-        isWidget={isWidget}
-      />
-      {!isWidget && (
-        <NodeModal
-          selectedNode={selectedNode}
-          visible={isModalVisible}
-          closeModal={() => setModalVisible(false)}
-        />
-      )}
-    </>
-  );
 };

+ 85 - 0
src/components/MonacoEditor/index.tsx

@@ -0,0 +1,85 @@
+import React from "react";
+import styled from "styled-components";
+import Editor, { loader } from "@monaco-editor/react";
+import { Loading } from "src/components/Loading";
+import { parser } from "src/utils/jsonParser";
+import useConfig from "src/hooks/store/useConfig";
+import useStored from "src/hooks/store/useStored";
+import useGraph from "src/hooks/store/useGraph";
+
+loader.config({
+  paths: {
+    vs: "https://microsoft.github.io/monaco-editor/node_modules/monaco-editor/min/vs",
+  },
+});
+
+const editorOptions = {
+  formatOnPaste: true,
+  minimap: {
+    enabled: false,
+  },
+};
+
+const StyledWrapper = styled.div`
+  display: grid;
+  height: calc(100vh - 36px);
+  grid-template-columns: 100%;
+  grid-template-rows: minmax(0, 1fr);
+`;
+
+export const MonacoEditor = ({
+  setHasError,
+}: {
+  setHasError: (value: boolean) => void;
+}) => {
+  const json = useConfig((state) => state.json);
+  const expand = useConfig((state) => state.expand);
+  const setJson = useConfig((state) => state.setJson);
+  const setGraphValue = useGraph((state) => state.setGraphValue);
+  const [value, setValue] = React.useState<string | undefined>("");
+
+  const lightmode = useStored((state) =>
+    state.lightmode ? "light" : "vs-dark"
+  );
+
+  React.useEffect(() => {
+    const { nodes, edges } = parser(json, expand);
+
+    setGraphValue("nodes", nodes);
+    setGraphValue("edges", edges);
+    setValue(json);
+  }, [expand, json, setGraphValue]);
+
+  React.useEffect(() => {
+    const formatTimer = setTimeout(() => {
+      try {
+        if (!value) {
+          setHasError(false);
+          return setJson("{}");
+        }
+
+        const parsedJSON = JSON.stringify(JSON.parse(value), null, 2);
+        setJson(parsedJSON);
+        setHasError(false);
+      } catch (jsonError: any) {
+        setHasError(true);
+      }
+    }, 1200);
+
+    return () => clearTimeout(formatTimer);
+  }, [value, setJson, setHasError]);
+
+  return (
+    <StyledWrapper>
+      <Editor
+        height="100%"
+        defaultLanguage="json"
+        value={value}
+        theme={lightmode}
+        options={editorOptions}
+        onChange={setValue}
+        loading={<Loading message="Loading Editor..." />}
+      />
+    </StyledWrapper>
+  );
+};

+ 3 - 70
src/containers/Editor/JsonEditor/index.tsx

@@ -1,16 +1,7 @@
 import React from "react";
 import styled from "styled-components";
-import MonacoEditor, { loader } from "@monaco-editor/react";
-import { ErrorContainer } from "src/components/ErrorContainer/ErrorContainer";
-import useConfig from "src/hooks/store/useConfig";
-import { Loading } from "src/components/Loading";
-import useStored from "src/hooks/store/useStored";
-
-loader.config({
-  paths: {
-    vs: "https://microsoft.github.io/monaco-editor/node_modules/monaco-editor/min/vs",
-  },
-});
+import { ErrorContainer } from "src/components/ErrorContainer";
+import { MonacoEditor } from "src/components/MonacoEditor";
 
 const StyledEditorWrapper = styled.div`
   display: flex;
@@ -19,71 +10,13 @@ const StyledEditorWrapper = styled.div`
   overflow: auto;
   user-select: none;
 `;
-
-const editorOptions = {
-  formatOnPaste: true,
-  minimap: {
-    enabled: false,
-  },
-};
-
-const StyledWrapper = styled.div`
-  display: grid;
-  height: calc(100vh - 36px);
-  grid-template-columns: 100%;
-  grid-template-rows: minmax(0, 1fr);
-`;
-
 export const JsonEditor: React.FC = () => {
-  const [value, setValue] = React.useState<string | undefined>("");
   const [hasError, setHasError] = React.useState(false);
 
-  const json = useConfig((state) => state.json);
-  const lightmode = useStored((state) => state.lightmode);
-  const setJson = useConfig((state) => state.setJson);
-
-  const editorTheme = React.useMemo(
-    () => (lightmode ? "light" : "vs-dark"),
-    [lightmode]
-  );
-
-  React.useEffect(() => {
-    setValue(JSON.stringify(JSON.parse(json), null, 2));
-  }, [json]);
-
-  React.useEffect(() => {
-    const formatTimer = setTimeout(() => {
-      try {
-        if (!value) {
-          setHasError(false);
-          return setJson("{}");
-        }
-
-        JSON.parse(value);
-        setJson(value);
-        setHasError(false);
-      } catch (jsonError: any) {
-        setHasError(true);
-      }
-    }, 1500);
-
-    return () => clearTimeout(formatTimer);
-  }, [value, setJson]);
-
   return (
     <StyledEditorWrapper>
       <ErrorContainer hasError={hasError} />
-      <StyledWrapper>
-        <MonacoEditor
-          height="100%"
-          defaultLanguage="json"
-          value={value}
-          theme={editorTheme}
-          options={editorOptions}
-          onChange={setValue}
-          loading={<Loading message="Loading Editor..." />}
-        />
-      </StyledWrapper>
+      <MonacoEditor setHasError={setHasError} />
     </StyledEditorWrapper>
   );
 };

+ 49 - 0
src/containers/Editor/LiveEditor/GraphCanvas.tsx

@@ -0,0 +1,49 @@
+import React from "react";
+import { NodeModal } from "src/containers/Modals/NodeModal";
+import useGraph from "src/hooks/store/useGraph";
+import { Graph } from "src/components/Graph";
+
+export const GraphCanvas = ({ isWidget = false }: { isWidget?: boolean }) => {
+  const [isModalVisible, setModalVisible] = React.useState(false);
+  const [selectedNode, setSelectedNode] = React.useState<[string, string][]>(
+    []
+  );
+
+  const openModal = React.useCallback(() => setModalVisible(true), []);
+
+  const collapsedNodes = useGraph((state) => state.collapsedNodes);
+  const collapsedEdges = useGraph((state) => state.collapsedEdges);
+
+  React.useEffect(() => {
+    const nodeList = collapsedNodes.map((id) => `[id$="node-${id}"]`);
+    const edgeList = collapsedEdges.map((id) => `[class$="edge-${id}"]`);
+
+    const hiddenItems = document.querySelectorAll(".hide");
+    hiddenItems.forEach((item) => item.classList.remove("hide"));
+
+    if (nodeList.length) {
+      const selectedNodes = document.querySelectorAll(nodeList.join(","));
+      const selectedEdges = document.querySelectorAll(edgeList.join(","));
+
+      selectedNodes.forEach((node) => node.classList.add("hide"));
+      selectedEdges.forEach((edge) => edge.classList.add("hide"));
+    }
+  }, [collapsedNodes, collapsedEdges]);
+
+  return (
+    <>
+      <Graph
+        openModal={openModal}
+        setSelectedNode={setSelectedNode}
+        isWidget={isWidget}
+      />
+      {!isWidget && (
+        <NodeModal
+          selectedNode={selectedNode}
+          visible={isModalVisible}
+          closeModal={() => setModalVisible(false)}
+        />
+      )}
+    </>
+  );
+};

+ 2 - 2
src/containers/Editor/LiveEditor/index.tsx

@@ -1,7 +1,7 @@
 import React from "react";
 import styled from "styled-components";
 import { Tools } from "src/containers/Editor/Tools";
-import { Graph } from "src/components/Graph";
+import { GraphCanvas } from "src/containers/Editor/LiveEditor/GraphCanvas";
 
 const StyledLiveEditor = styled.div`
   position: relative;
@@ -11,7 +11,7 @@ const LiveEditor: React.FC = () => {
   return (
     <StyledLiveEditor>
       <Tools />
-      <Graph />
+      <GraphCanvas />
     </StyledLiveEditor>
   );
 };

+ 7 - 5
src/containers/Modals/ShareModal/index.tsx

@@ -51,11 +51,13 @@ export const ShareModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
   const shareURL = `${baseURL}/editor?json=${encodedJson}`;
 
   React.useEffect(() => {
-    const jsonEncode = compress(JSON.parse(json));
-    const jsonString = JSON.stringify(jsonEncode);
-
-    setEncodedJson(encodeURIComponent(jsonString));
-  }, [json]);
+    if (visible) {
+      const jsonEncode = compress(JSON.parse(json));
+      const jsonString = JSON.stringify(jsonEncode);
+  
+      setEncodedJson(encodeURIComponent(jsonString));
+    }
+  }, [json, visible]);
 
   const handleShare = (value: string) => {
     navigator.clipboard.writeText(value);

+ 0 - 1
src/hooks/store/useGraph.tsx

@@ -1,5 +1,4 @@
 import create from "zustand";
-import { Graph } from "src/components/Graph";
 import { getChildrenEdges } from "src/utils/getChildrenEdges";
 import { getOutgoers } from "src/utils/getOutgoers";