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