fix: performance
This commit is contained in:
@@ -188,8 +188,21 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||
const [isSelecting, setIsSelecting] = React.useState(false)
|
||||
const [ariaAnnouncement, setAriaAnnouncement] = React.useState<string | null>(null)
|
||||
const [fullscreenNodeId, setFullscreenNodeId] = React.useState<string | null>(null)
|
||||
|
||||
const graphApplyTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const GRAPH_APPLY_DEBOUNCE_MS = 300
|
||||
useEffect(() => {
|
||||
dispatchCanvasCommand({ type: 'graph/apply', payload: { nodes, edges } })
|
||||
if (graphApplyTimeoutRef.current) clearTimeout(graphApplyTimeoutRef.current)
|
||||
graphApplyTimeoutRef.current = setTimeout(() => {
|
||||
graphApplyTimeoutRef.current = null
|
||||
dispatchCanvasCommand({ type: 'graph/apply', payload: { nodes, edges } })
|
||||
}, GRAPH_APPLY_DEBOUNCE_MS)
|
||||
return () => {
|
||||
if (graphApplyTimeoutRef.current) {
|
||||
clearTimeout(graphApplyTimeoutRef.current)
|
||||
graphApplyTimeoutRef.current = null
|
||||
}
|
||||
}
|
||||
}, [nodes, edges])
|
||||
|
||||
const connectionPath = useCanvasConnectionPathFromStore()
|
||||
@@ -218,7 +231,22 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||
rafRef.current = requestAnimationFrame(() => {
|
||||
rafRef.current = null
|
||||
const toApply = pendingChangesRef.current.splice(0, pendingChangesRef.current.length)
|
||||
if (toApply.length > 0) setNodesSilent((nds) => applyNodeChanges(toApply, nds))
|
||||
if (toApply.length === 0) return
|
||||
setNodesSilent((nds) => {
|
||||
// Drop dimension-only changes that don't change the node (e.g. React Flow re-reporting on visibility).
|
||||
// Avoids graph/apply and store churn when nodes become visible with onlyRenderVisibleElements.
|
||||
const filtered = toApply.filter((c) => {
|
||||
const ch = c as NodeChange<Node> & { type?: string; dimensions?: { width?: number; height?: number } }
|
||||
if (ch.type !== 'dimensions' || ch.dimensions == null) return true
|
||||
const node = nds.find((n) => n.id === (ch as { id?: string }).id)
|
||||
if (!node) return true
|
||||
const nw = (node as Node & { width?: number }).width
|
||||
const nh = (node as Node & { height?: number }).height
|
||||
return nw !== ch.dimensions.width || nh !== ch.dimensions.height
|
||||
})
|
||||
if (filtered.length === 0) return nds
|
||||
return applyNodeChanges(filtered, nds)
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -38,6 +38,8 @@ export type RenderingNodeData = {
|
||||
const DEFAULT_VIEWPORT_WIDTH = 1200
|
||||
const DEFAULT_VIEWPORT_HEIGHT = 800
|
||||
const RENDER_DEBOUNCE_MS = 250
|
||||
/** Skip auto-run when we have cache and effect runs soon after mount (node became visible). */
|
||||
const VISIBILITY_GRACE_MS = 400
|
||||
|
||||
/** Lifecycle state to pass to useSyncConnectionStatus so edge status (updating/error) stays in sync. */
|
||||
export type RenderingNodeLifecycle = {
|
||||
@@ -194,6 +196,8 @@ export function useRenderingNodeState(
|
||||
const minLoadingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const lastManualRunTriggerRef = useRef(0)
|
||||
const manualRunTriggerSyncedRef = useRef(false)
|
||||
/** In auto mode, skip running when we have cache and we're in the grace window after mount (node became visible). */
|
||||
const mountTimeRef = useRef<number>(0)
|
||||
|
||||
const updateDataRef = useRef(updateData)
|
||||
updateDataRef.current = updateData
|
||||
@@ -214,6 +218,7 @@ export function useRenderingNodeState(
|
||||
useSyncConnectionStatus(id, lifecycle)
|
||||
|
||||
useEffect(() => {
|
||||
if (mountTimeRef.current === 0) mountTimeRef.current = Date.now()
|
||||
if (incomingIds.length === 0) {
|
||||
setRenderedContent(null)
|
||||
setResolvedContent(null)
|
||||
@@ -258,6 +263,18 @@ export function useRenderingNodeState(
|
||||
lastManualRunTriggerRef.current = runTrigger
|
||||
}
|
||||
|
||||
// Auto mode: do not run when node just became visible (remount with cache). No pipeline run, no path/addTrigger.
|
||||
if (effectiveUpdateMode === 'auto') {
|
||||
const hasCache = Boolean(
|
||||
(data?.cachedRenderedContent ?? data?.cachedResolvedContent) as string | undefined
|
||||
)
|
||||
const signatureUnchanged =
|
||||
data?.lastRunSourceSignature != null && data.lastRunSourceSignature === sourceSignature
|
||||
const withinVisibilityGrace =
|
||||
mountTimeRef.current > 0 && Date.now() - mountTimeRef.current < VISIBILITY_GRACE_MS
|
||||
if (hasCache && (signatureUnchanged || withinVisibilityGrace)) return
|
||||
}
|
||||
|
||||
runIdRef.current += 1
|
||||
const thisRunId = runIdRef.current
|
||||
const signatureForThisRun = sourceSignature
|
||||
|
||||
Reference in New Issue
Block a user