Enhance ObjectSelect Component with Improved State Management and Value Handling
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Introduced refs for loaded keys and tree select value to optimize state management and prevent unnecessary re-renders. - Refactored getValueIdentity function to handle various data types more effectively, including null and boolean values. - Updated selection handling logic to ensure accurate comparisons and prevent redundant updates during value changes. - Enhanced loading logic to track loaded nodes and improve the rendering of child components based on selection state. - Implemented additional useEffect hooks to synchronize state changes with component updates, enhancing overall responsiveness.
This commit is contained in:
parent
da2881d505
commit
968dc0667a
@ -251,6 +251,8 @@ const ObjectSelect = ({
|
||||
const masterFilterRef = useRef(masterFilter)
|
||||
const clearedMissingValueRef = useRef(false)
|
||||
const loadGenerationRef = useRef(0)
|
||||
const loadedKeysRef = useRef(new Set())
|
||||
const treeSelectValueRef = useRef(null)
|
||||
const getSelectKey = useCallback(
|
||||
(selectType, selectMasterFilter) =>
|
||||
`${selectType}::${JSON.stringify(selectMasterFilter ?? {})}`,
|
||||
@ -269,31 +271,25 @@ const ObjectSelect = ({
|
||||
|
||||
// Normalize a value to an identity string so we can detect in-place _id updates
|
||||
const getValueIdentity = useCallback((val) => {
|
||||
if (val && typeof val === 'object') {
|
||||
// Handle arrays
|
||||
if (val == null || val === '') return ''
|
||||
if (
|
||||
typeof val === 'string' ||
|
||||
typeof val === 'number' ||
|
||||
typeof val === 'boolean'
|
||||
) {
|
||||
return String(val).toLowerCase()
|
||||
}
|
||||
if (typeof val !== 'object') return String(val)
|
||||
|
||||
if (Array.isArray(val)) {
|
||||
const ids = val
|
||||
.map((item) => {
|
||||
if (item && typeof item === 'object') {
|
||||
if (item._id) return String(item._id)
|
||||
if (
|
||||
item.value &&
|
||||
typeof item.value === 'object' &&
|
||||
item.value._id
|
||||
)
|
||||
return String(item.value._id)
|
||||
}
|
||||
return null
|
||||
})
|
||||
.map((item) => getValueIdentity(item))
|
||||
.filter(Boolean)
|
||||
.sort()
|
||||
return ids.length > 0 ? ids.join(',') : JSON.stringify(val)
|
||||
}
|
||||
// Handle single objects
|
||||
if (val._id) return String(val._id)
|
||||
if (val.value && typeof val.value === 'object' && val.value._id)
|
||||
return String(val.value._id)
|
||||
return ids.join(',')
|
||||
}
|
||||
if (val._id) return String(val._id).toLowerCase()
|
||||
if (val.value != null) return getValueIdentity(val.value)
|
||||
return JSON.stringify(val)
|
||||
}, [])
|
||||
const prevValueIdentityRef = useRef(getValueIdentity(value))
|
||||
@ -426,11 +422,7 @@ const ObjectSelect = ({
|
||||
masterFilterRef.current
|
||||
)
|
||||
) {
|
||||
if (itemExists) {
|
||||
reloadRef.current?.()
|
||||
} else {
|
||||
silentReloadRef.current?.()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -445,7 +437,7 @@ const ObjectSelect = ({
|
||||
}, [])
|
||||
|
||||
const newEventHandler = useCallback(() => {
|
||||
reloadRef.current?.()
|
||||
silentReloadRef.current?.()
|
||||
}, [])
|
||||
|
||||
const reloadRef = useRef(null)
|
||||
@ -536,8 +528,8 @@ const ObjectSelect = ({
|
||||
newFilterPath,
|
||||
objects
|
||||
)
|
||||
const resolvedChildren =
|
||||
nodeChildren.length === 0 ? undefined : nodeChildren
|
||||
const loaded =
|
||||
nodeChildren.length > 0 || loadedKeysRef.current.has(nodeKey)
|
||||
const modelProperty = getModelProperty(type, property)
|
||||
return {
|
||||
title: <ObjectProperty {...modelProperty} value={value} />,
|
||||
@ -549,7 +541,8 @@ const ObjectSelect = ({
|
||||
filterPath: newFilterPath,
|
||||
selectable: false,
|
||||
isLeaf: false,
|
||||
children: resolvedChildren
|
||||
loaded,
|
||||
children: loaded ? nodeChildren : undefined
|
||||
}
|
||||
})
|
||||
.filter(Boolean)
|
||||
@ -608,6 +601,7 @@ const ObjectSelect = ({
|
||||
async (node) => {
|
||||
if (!node.property) return
|
||||
if (type == 'unknown') return
|
||||
if (node.key) loadedKeysRef.current.add(node.key)
|
||||
await handleFetchObjectsProperties(buildFilterFromNode(node))
|
||||
},
|
||||
[buildFilterFromNode, handleFetchObjectsProperties, type]
|
||||
@ -696,6 +690,13 @@ const ObjectSelect = ({
|
||||
|
||||
const onTreeSelectChange = useCallback(
|
||||
(nextValue) => {
|
||||
if (
|
||||
getValueIdentity(nextValue) ===
|
||||
getValueIdentity(treeSelectValueRef.current)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
const isEmptySelection = multiple
|
||||
? !Array.isArray(nextValue) || nextValue.length === 0
|
||||
: nextValue == null || nextValue === ''
|
||||
@ -744,7 +745,7 @@ const ObjectSelect = ({
|
||||
}
|
||||
onChange?.(null)
|
||||
},
|
||||
[multiple, onChange, findObjectById]
|
||||
[multiple, onChange, findObjectById, getValueIdentity]
|
||||
)
|
||||
|
||||
const onSearch = useCallback(
|
||||
@ -856,6 +857,10 @@ const ObjectSelect = ({
|
||||
treeDataRef.current = treeData
|
||||
}, [treeData])
|
||||
|
||||
useEffect(() => {
|
||||
treeSelectValueRef.current = treeSelectValue
|
||||
}, [treeSelectValue])
|
||||
|
||||
const prevValuesRef = useRef({ type, masterFilter })
|
||||
|
||||
useEffect(() => {
|
||||
@ -877,6 +882,7 @@ const ObjectSelect = ({
|
||||
treeDataRef.current = []
|
||||
setTreeVersion((v) => v + 1)
|
||||
setExpandedKeys([])
|
||||
loadedKeysRef.current = new Set()
|
||||
setInitialized(false)
|
||||
valueRef.current = null
|
||||
setTreeSelectValue(null)
|
||||
@ -899,13 +905,16 @@ const ObjectSelect = ({
|
||||
const changeSource = isInternalChangeRef.current ? 'internal' : 'external'
|
||||
|
||||
if (changeSource == 'external') {
|
||||
const nextId = getValueId(value)
|
||||
const alreadyInTree = isValueInTree(treeDataRef.current, nextId)
|
||||
const alreadySelected =
|
||||
treeSelectValueRef.current != null &&
|
||||
getValueIdentity(treeSelectValueRef.current) === currentValueIdentity
|
||||
|
||||
if (!alreadyInTree && !alreadySelected) {
|
||||
loadGenerationRef.current += 1
|
||||
setObjectPropertiesTree({})
|
||||
setTreeData([])
|
||||
treeDataRef.current = []
|
||||
setInitialized(false)
|
||||
setInitialLoading(true)
|
||||
valueRef.current = null
|
||||
}
|
||||
|
||||
clearedMissingValueRef.current = false
|
||||
setValueNotFound(false)
|
||||
}
|
||||
@ -924,6 +933,40 @@ const ObjectSelect = ({
|
||||
const generation = loadGenerationRef.current
|
||||
const handleValue = async () => {
|
||||
if (generation !== loadGenerationRef.current) return
|
||||
|
||||
const valueIdentity = getValueIdentity(value)
|
||||
const ids = multiple
|
||||
? (Array.isArray(value) ? value.map(getValueId) : [])
|
||||
: value == null
|
||||
? []
|
||||
: [getValueId(value)]
|
||||
const allInTree =
|
||||
ids.length > 0 &&
|
||||
ids.every(
|
||||
(id) =>
|
||||
id == null ||
|
||||
id === '' ||
|
||||
isValueInTree(treeDataRef.current, id) ||
|
||||
findObjectById(id) != null
|
||||
)
|
||||
|
||||
if (
|
||||
value != null &&
|
||||
type != 'unknown' &&
|
||||
allInTree &&
|
||||
getValueIdentity(valueRef.current) !== valueIdentity
|
||||
) {
|
||||
valueRef.current = value
|
||||
setTreeSelectValue(
|
||||
multiple
|
||||
? ids.map((id) => toSelectValue(id)).filter((id) => id != null)
|
||||
: toSelectValue(ids[0])
|
||||
)
|
||||
setInitialized(true)
|
||||
setInitialLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
multiple &&
|
||||
Array.isArray(value) &&
|
||||
@ -975,6 +1018,7 @@ const ObjectSelect = ({
|
||||
}
|
||||
|
||||
setExpandedKeys([...new Set(pathKeys)])
|
||||
pathKeys.forEach((key) => loadedKeysRef.current.add(key))
|
||||
setTreeSelectValue(
|
||||
value
|
||||
.map((item) => toSelectValue(getValueId(item)))
|
||||
@ -1025,6 +1069,7 @@ const ObjectSelect = ({
|
||||
}
|
||||
})
|
||||
setExpandedKeys(pathKeys)
|
||||
pathKeys.forEach((key) => loadedKeysRef.current.add(key))
|
||||
const data = await handleFetchObjectsProperties(valueFilter)
|
||||
if (generation !== loadGenerationRef.current) return
|
||||
if (Array.isArray(data)) applyTreeFromData(data)
|
||||
@ -1069,7 +1114,8 @@ const ObjectSelect = ({
|
||||
connected,
|
||||
getValueIdentity,
|
||||
multiple,
|
||||
applyTreeFromData
|
||||
applyTreeFromData,
|
||||
findObjectById
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user