From 16a9caa555a2d63b7ff8af0731fbfd3231d6225c Mon Sep 17 00:00:00 2001
From: Jacky Zhao <j.zhao2k19@gmail.com>
Date: Mon, 16 Sep 2024 01:05:17 +0000
Subject: [PATCH] perf: eagerly compute explorer nodes to avoid re-render in memoized value

---
 quartz/components/ExplorerNode.tsx |   13 ++----
 quartz/components/Explorer.tsx     |   76 ++++++++++++++++++++------------------
 2 files changed, 44 insertions(+), 45 deletions(-)

diff --git a/quartz/components/Explorer.tsx b/quartz/components/Explorer.tsx
index ec7c48e..df876ab 100644
--- a/quartz/components/Explorer.tsx
+++ b/quartz/components/Explorer.tsx
@@ -7,6 +7,7 @@
 import { QuartzPluginData } from "../plugins/vfile"
 import { classNames } from "../util/lang"
 import { i18n } from "../i18n"
+import { VNode } from "preact"
 
 // Options interface defined in `ExplorerNode` to avoid circular dependency
 const defaultOptions = {
@@ -44,6 +45,7 @@
   // memoized
   let fileTree: FileNode
   let jsonTree: string
+  let component: VNode
   let lastBuildId: string = ""
 
   function constructFileTree(allFiles: QuartzPluginData[]) {
@@ -82,44 +84,46 @@
     if (ctx.buildId !== lastBuildId) {
       lastBuildId = ctx.buildId
       constructFileTree(allFiles)
+      const tree = ExplorerNode({ node: fileTree, opts, fileData })
+      component = (
+        <div class={classNames(displayClass, "explorer")}>
+          <button
+            type="button"
+            id="explorer"
+            data-behavior={opts.folderClickBehavior}
+            data-collapsed={opts.folderDefaultState}
+            data-savestate={opts.useSavedState}
+            data-tree={jsonTree}
+            aria-controls="explorer-content"
+            aria-expanded={opts.folderDefaultState === "open"}
+          >
+            <h2>{opts.title ?? i18n(cfg.locale).components.explorer.title}</h2>
+            <svg
+              xmlns="http://www.w3.org/2000/svg"
+              width="14"
+              height="14"
+              viewBox="5 8 14 8"
+              fill="none"
+              stroke="currentColor"
+              stroke-width="2"
+              stroke-linecap="round"
+              stroke-linejoin="round"
+              class="fold"
+            >
+              <polyline points="6 9 12 15 18 9"></polyline>
+            </svg>
+          </button>
+          <div id="explorer-content">
+            <ul class="overflow" id="explorer-ul">
+              {tree}
+              <li id="explorer-end" />
+            </ul>
+          </div>
+        </div>
+      )
     }
 
-    return (
-      <div class={classNames(displayClass, "explorer")}>
-        <button
-          type="button"
-          id="explorer"
-          data-behavior={opts.folderClickBehavior}
-          data-collapsed={opts.folderDefaultState}
-          data-savestate={opts.useSavedState}
-          data-tree={jsonTree}
-          aria-controls="explorer-content"
-          aria-expanded={opts.folderDefaultState === "open"}
-        >
-          <h2>{opts.title ?? i18n(cfg.locale).components.explorer.title}</h2>
-          <svg
-            xmlns="http://www.w3.org/2000/svg"
-            width="14"
-            height="14"
-            viewBox="5 8 14 8"
-            fill="none"
-            stroke="currentColor"
-            stroke-width="2"
-            stroke-linecap="round"
-            stroke-linejoin="round"
-            class="fold"
-          >
-            <polyline points="6 9 12 15 18 9"></polyline>
-          </svg>
-        </button>
-        <div id="explorer-content">
-          <ul class="overflow" id="explorer-ul">
-            <ExplorerNode node={fileTree} opts={opts} fileData={fileData} />
-            <li id="explorer-end" />
-          </ul>
-        </div>
-      </div>
-    )
+    return component
   }
 
   Explorer.css = explorerStyle
diff --git a/quartz/components/ExplorerNode.tsx b/quartz/components/ExplorerNode.tsx
index e57d677..3137a3f 100644
--- a/quartz/components/ExplorerNode.tsx
+++ b/quartz/components/ExplorerNode.tsx
@@ -224,15 +224,10 @@
               class="content"
               data-folderul={folderPath}
             >
-              {node.children.map((childNode, i) => (
-                <ExplorerNode
-                  node={childNode}
-                  key={i}
-                  opts={opts}
-                  fullPath={folderPath}
-                  fileData={fileData}
-                />
-              ))}
+              {node.children.map((childNode) =>
+                // eagerly render children so we can memoize properly
+                ExplorerNode({ node: childNode, opts, fileData, fullPath: folderPath }),
+              )}
             </ul>
           </div>
         </li>

--
Gitblit v1.10.0