diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..7100e04 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-05-19 - useDeferredValue for rapidly updating UI +**Learning:** During text streaming with very short intervals (e.g. 24ms), synchronous operations on the changing state (like expensive calculations or text analysis) will block the main thread and cause the UI to become unresponsive. +**Action:** Use React's `useDeferredValue` for the rapidly changing state and perform the expensive synchronous computations on the deferred value to ensure the main thread isn't blocked. diff --git a/apps/quill/client/components/playground-view.tsx b/apps/quill/client/components/playground-view.tsx index 511226b..9b46903 100644 --- a/apps/quill/client/components/playground-view.tsx +++ b/apps/quill/client/components/playground-view.tsx @@ -1,6 +1,6 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { AnimatePresence, motion } from "framer-motion"; -import { useEffect, useMemo, useRef, useState } from "react"; +import { useDeferredValue, useEffect, useMemo, useRef, useState } from "react"; import { USE_CASE_PRESETS } from "../../src/lib/presets"; import { analyzeText, scoreDeterministic } from "../../src/lib/rubric"; import type { Guide, UseCase } from "../../src/lib/types"; @@ -255,7 +255,14 @@ export function PlaygroundView({ }; }, [output]); - const snapshot = useMemo(() => analyzeText(visibleOutput), [visibleOutput]); + // Defer the rapidly changing visibleOutput so the expensive text analysis + // doesn't block the main thread during the 24ms interval streaming. + const deferredVisibleOutput = useDeferredValue(visibleOutput); + + const snapshot = useMemo( + () => analyzeText(deferredVisibleOutput), + [deferredVisibleOutput] + ); const { score, details } = useMemo( () => guide