From 545cc0c52675fb672e6fc0e10f52690e3f22437c Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Fri, 6 Mar 2026 23:43:15 +0000 Subject: [PATCH] Refactor ObjectDisplay component to enhance ID validation and handling. Introduced utility functions for ID extraction and validation, ensuring proper object hydration and subscription management. Updated related logic to improve robustness against invalid IDs. --- .../Dashboard/common/ObjectDisplay.jsx | 52 ++++++++++++++----- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/src/components/Dashboard/common/ObjectDisplay.jsx b/src/components/Dashboard/common/ObjectDisplay.jsx index 56ac041..045a807 100644 --- a/src/components/Dashboard/common/ObjectDisplay.jsx +++ b/src/components/Dashboard/common/ObjectDisplay.jsx @@ -31,12 +31,33 @@ const ObjectDisplay = ({ setObjectData((prev) => merge({}, prev, value)) }, []) - // Detect minimal objects that only contain an _id + // Ensure ID is valid before hydrate/subscribe (non-empty, not null/undefined) + const isValidId = useCallback((id) => { + return id != null && String(id).trim() !== '' + }, []) + + // Extract string ID from object; handles both primitive _id and populated ref (_id as object) + const getStringId = useCallback((obj) => { + const id = obj?._id + if (id == null) return null + if (typeof id === 'string') return id + if (typeof id === 'object' && id !== null && typeof id._id === 'string') + return id._id + return null + }, []) + + // Detect minimal objects that only contain an _id (must be string, not populated object) const isMinimalObject = useCallback((obj) => { if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return false const keys = Object.keys(obj) - return keys.length === 1 && keys[0] === '_id' && obj._id - }, []) + const id = obj._id + return ( + keys.length === 1 && + keys[0] === '_id' && + typeof id === 'string' && + isValidId(id) + ) + }, [isValidId]) // If only an _id is provided, fetch the full object via spotlight const fetchFullObjectIfNeeded = useCallback( @@ -64,9 +85,10 @@ const ObjectDisplay = ({ // Subscribe to object updates when component mounts useEffect(() => { - if (object?._id && objectType && connected && token != null) { + const id = getStringId(object) + if (isValidId(id) && objectType && connected && token != null) { const objectUpdatesUnsubscribe = subscribeToObjectUpdates( - object._id, + id, objectType, updateObjectEventHandler ) @@ -76,27 +98,31 @@ const ObjectDisplay = ({ } } }, [ - object?._id, + object, objectType, subscribeToObjectUpdates, connected, token, - updateObjectEventHandler + updateObjectEventHandler, + isValidId, + getStringId ]) // Update local state when object prop changes useEffect(() => { if (token == null) return + const id = getStringId(object) + if (!isValidId(id)) return const isMinimal = isMinimalObject(object) // Only skip re-fetch when we have a minimal object and already hydrated this id - if (isMinimal && idRef.current === object?._id) return + if (isMinimal && idRef.current === id) return let cancelled = false if (isMinimal) setIsHydrating(true) const hydrateObject = async () => { const fullObject = await fetchFullObjectIfNeeded(object) if (!cancelled) { setObjectData((prev) => merge({}, prev, fullObject)) - if (isMinimal) idRef.current = object?._id + if (isMinimal) idRef.current = id setIsHydrating(false) } } @@ -105,7 +131,7 @@ const ObjectDisplay = ({ cancelled = true setIsHydrating(false) } - }, [object, fetchFullObjectIfNeeded, isMinimalObject, token]) + }, [object, fetchFullObjectIfNeeded, isMinimalObject, isValidId, getStringId, token]) if (!objectData) { return n/a } @@ -137,7 +163,7 @@ const ObjectDisplay = ({ var hyperlink = null const defaultModelActions = model.actions?.filter((action) => action.default == true) || [] - const objectId = objectData._id + const objectId = getStringId(objectData) if (defaultModelActions.length >= 1 && objectId) { hyperlink = defaultModelActions[0].url(objectId) @@ -229,9 +255,9 @@ const ObjectDisplay = ({
{renderNameDisplay()} - {objectData?._id && !objectData?.name ? ( + {objectId && !objectData?.name ? (