فهرست منبع

performance improvements

AykutSarac 2 سال پیش
والد
کامیت
0c75607902

+ 10 - 25
src/components/ErrorContainer/ErrorContainer.tsx

@@ -7,16 +7,6 @@ import {
   MdOutlineCheckCircleOutline,
 } from "react-icons/md";
 
-export type Error = {
-  message: string;
-  isExpanded: boolean;
-};
-
-interface ErrorContainerProps {
-  error: Error;
-  setError: React.Dispatch<React.SetStateAction<Error>>;
-}
-
 const StyledErrorWrapper = styled.div`
   z-index: 1;
 `;
@@ -61,36 +51,31 @@ const StyledError = styled.pre`
   white-space: pre-line;
 `;
 
-export const ErrorContainer: React.FC<ErrorContainerProps> = ({
-  error,
-  setError,
-}) => {
+export const ErrorContainer = ({ error }: { error: string }) => {
+  const [isErrorExpanded, setErrorExpanded] = React.useState(true);
+
   return (
     <StyledErrorWrapper>
       <StyledErrorExpand
-        error={!!error.message}
-        onClick={() =>
-          setError((err: Error) => ({ ...err, isExpanded: !err.isExpanded }))
-        }
+        error={!!error.length}
+        onClick={() => setErrorExpanded(!isErrorExpanded)}
       >
         <StyledTitle>
-          {error.message ? (
+          {error ? (
             <MdReportGmailerrorred size={20} />
           ) : (
             <MdOutlineCheckCircleOutline size={20} />
           )}
-          {error.message ? "Error" : "JSON Valid"}
+          {error ? "Error" : "JSON Valid"}
         </StyledTitle>
-        {error.message &&
-          (error.isExpanded ? (
+        {error &&
+          (isErrorExpanded ? (
             <MdExpandLess size={22} />
           ) : (
             <MdExpandMore size={22} />
           ))}
       </StyledErrorExpand>
-      {error.isExpanded && error.message && (
-        <StyledError>{error.message}</StyledError>
-      )}
+      {isErrorExpanded && error && <StyledError>{error}</StyledError>}
     </StyledErrorWrapper>
   );
 };

+ 18 - 29
src/components/Graph/index.tsx

@@ -4,14 +4,7 @@ import {
   TransformComponent,
   TransformWrapper,
 } from "react-zoom-pan-pinch";
-import {
-  Canvas,
-  CanvasContainerProps,
-  EdgeData,
-  ElkRoot,
-  NodeData,
-  NodeProps,
-} from "reaflow";
+import { Canvas, EdgeData, ElkRoot, NodeData, NodeProps } from "reaflow";
 import { CustomNode } from "src/components/CustomNode";
 import { NodeModal } from "src/containers/Modals/NodeModal";
 import { getEdgeNodes } from "src/containers/Editor/LiveEditor/helpers";
@@ -20,7 +13,6 @@ import styled from "styled-components";
 import shallow from "zustand/shallow";
 
 interface LayoutProps {
-  json: string;
   isWidget: boolean;
   openModal: () => void;
   setSelectedNode: (node: object) => void;
@@ -42,11 +34,11 @@ const StyledEditorWrapper = styled.div<{ isWidget: boolean }>`
 `;
 
 const MemoizedGraph = React.memo(function Layout({
-  json,
   isWidget,
   openModal,
   setSelectedNode,
 }: LayoutProps) {
+  const json = useConfig((state) => state.json);
   const [nodes, setNodes] = React.useState<NodeData[]>([]);
   const [edges, setEdges] = React.useState<EdgeData[]>([]);
   const [size, setSize] = React.useState({
@@ -81,13 +73,20 @@ const MemoizedGraph = React.memo(function Layout({
       setSize({ width: layout.width, height: layout.height });
   };
 
-  const handleNodeClick = (
-    e: React.MouseEvent<SVGElement>,
-    props: NodeProps
-  ) => {
-    setSelectedNode(props.properties.text);
-    openModal();
-  };
+  const handleNodeClick = React.useCallback(
+    (e: React.MouseEvent<SVGElement>, props: NodeProps) => {
+      setSelectedNode(props.properties.text);
+      openModal();
+    },
+    [openModal, setSelectedNode]
+  );
+
+  const node = React.useCallback(
+    (props) => (
+      <CustomNode onClick={(e) => handleNodeClick(e, props)} {...props} />
+    ),
+    [handleNodeClick]
+  );
 
   return (
     <StyledEditorWrapper isWidget={isWidget}>
@@ -114,14 +113,8 @@ const MemoizedGraph = React.memo(function Layout({
             maxHeight={size.height + 100}
             direction={layout}
             key={layout}
-            onCanvasClick={onCanvasClick}
             onLayoutChange={onLayoutChange}
-            node={(props) => (
-              <CustomNode
-                onClick={(e) => handleNodeClick(e, props)}
-                {...props}
-              />
-            )}
+            node={node}
             zoomable={false}
             readonly
           />
@@ -131,10 +124,7 @@ const MemoizedGraph = React.memo(function Layout({
   );
 });
 
-export const Graph: React.FC<{ json: string; isWidget?: boolean }> = ({
-  json,
-  isWidget = false,
-}) => {
+export const Graph = ({ isWidget = false }: { isWidget?: boolean }) => {
   const [isModalVisible, setModalVisible] = React.useState(false);
   const [selectedNode, setSelectedNode] = React.useState<object>({});
 
@@ -143,7 +133,6 @@ export const Graph: React.FC<{ json: string; isWidget?: boolean }> = ({
   return (
     <>
       <MemoizedGraph
-        json={json}
         openModal={openModal}
         setSelectedNode={setSelectedNode}
         isWidget={isWidget}

+ 2 - 2
src/components/Sidebar/index.tsx

@@ -136,7 +136,7 @@ const StyledAlertIcon = styled(IoAlertCircleSharp)`
 `;
 
 export const Sidebar: React.FC = () => {
-  const json = useConfig((state) => state.json);
+  const getJson = useConfig((state) => state.getJson);
   const updateSetting = useConfig((state) => state.updateSetting);
 
   const { expand, performance, layout } = useConfig((state) => state.settings);
@@ -147,7 +147,7 @@ export const Sidebar: React.FC = () => {
 
   const handleSave = () => {
     const a = document.createElement("a");
-    const file = new Blob([json], { type: "text/plain" });
+    const file = new Blob([getJson()], { type: "text/plain" });
 
     a.href = window.URL.createObjectURL(file);
     a.download = "jsonvisio.json";

+ 9 - 13
src/containers/Editor/JsonEditor/index.tsx

@@ -6,7 +6,6 @@ import { 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 { CarbonAds } from "src/components/CarbonAds";
 
 loader.config({
   paths: {
@@ -37,16 +36,13 @@ const StyledWrapper = styled.div`
 `;
 
 export const JsonEditor: React.FC = () => {
+  const [value, setValue] = React.useState<string | undefined>("");
+  const [error, setError] = React.useState("");
+
   const json = useConfig((state) => state.json);
   const lightmode = useConfig((state) => state.settings.lightmode);
   const updateJson = useConfig((state) => state.updateJson);
 
-  const [value, setValue] = React.useState("");
-  const [error, setError] = React.useState({
-    message: "",
-    isExpanded: true,
-  });
-
   const editorTheme = React.useMemo(
     () => (lightmode ? "light" : "vs-dark"),
     [lightmode]
@@ -60,15 +56,15 @@ export const JsonEditor: React.FC = () => {
     const formatTimer = setTimeout(() => {
       try {
         if (!value) {
-          setError((err) => ({ ...err, message: "" }));
-          return updateJson("[]");
+          setError("");
+          return updateJson("{}");
         }
 
         parseJson(value);
         updateJson(value);
-        setError((err) => ({ ...err, message: "" }));
+        setError("");
       } catch (jsonError: any) {
-        setError((err) => ({ ...err, message: jsonError.message }));
+        setError(jsonError.message);
       }
     }, 1500);
 
@@ -77,7 +73,7 @@ export const JsonEditor: React.FC = () => {
 
   return (
     <StyledEditorWrapper>
-      <ErrorContainer error={error} setError={setError} />
+      <ErrorContainer error={error} />
       <StyledWrapper>
         <Editor
           height="100%"
@@ -85,8 +81,8 @@ export const JsonEditor: React.FC = () => {
           value={value}
           theme={editorTheme}
           options={editorOptions}
+          onChange={setValue}
           loading={<Loading message="Loading Editor..." />}
-          onChange={(value) => setValue(value as string)}
         />
       </StyledWrapper>
     </StyledEditorWrapper>

+ 1 - 4
src/containers/Editor/LiveEditor/index.tsx

@@ -2,19 +2,16 @@ import React from "react";
 import styled from "styled-components";
 import { Tools } from "src/containers/Editor/Tools";
 import { Graph } from "src/components/Graph";
-import useConfig from "src/hooks/store/useConfig";
 
 const StyledLiveEditor = styled.div`
   position: relative;
 `;
 
 const LiveEditor: React.FC = () => {
-  const json = useConfig((state) => state.json);
-
   return (
     <StyledLiveEditor>
       <Tools />
-      <Graph json={json} />
+      <Graph />
     </StyledLiveEditor>
   );
 };

+ 2 - 0
src/hooks/store/useConfig.tsx

@@ -18,6 +18,7 @@ function getTomorrow() {
 
 export interface Config {
   json: string;
+  getJson: () => string;
   settings: StorageConfig;
   sponsors: {
     users: Sponsor[];
@@ -36,6 +37,7 @@ const useConfig = create(
   persist<Config>(
     (set, get) => ({
       json: JSON.stringify(defaultJson),
+      getJson: () => get().json,
       settings: defaultConfig,
       sponsors: null,
       setSponsors: (users) =>