Spaces:
Paused
Paused
import React from 'react'; | |
/** | |
* Updates a deeply nested value in an object using a string path | |
* @param obj The object to update | |
* @param value The new value to set | |
* @param path String path to the property (e.g. 'config.process[0].model.name_or_path') | |
* @returns A new object with the updated value | |
*/ | |
export function setNestedValue<T, V>(obj: T, value: V, path?: string): T { | |
// Create a copy of the original object to maintain immutability | |
const result = { ...obj }; | |
// if path is not provided, be root path | |
if (!path) { | |
path = ''; | |
} | |
// Split the path into segments | |
const pathArray = path.split('.').flatMap(segment => { | |
// Handle array notation like 'process[0]' | |
const arrayMatch = segment.match(/^([^\[]+)(\[\d+\])+/); | |
if (arrayMatch) { | |
const propName = arrayMatch[1]; | |
const indices = segment | |
.substring(propName.length) | |
.match(/\[(\d+)\]/g) | |
?.map(idx => parseInt(idx.substring(1, idx.length - 1))); | |
// Return property name followed by array indices | |
return [propName, ...(indices || [])]; | |
} | |
return segment; | |
}); | |
// Navigate to the target location | |
let current: any = result; | |
for (let i = 0; i < pathArray.length - 1; i++) { | |
const key = pathArray[i]; | |
// If current key is a number, treat it as an array index | |
if (typeof key === 'number') { | |
if (!Array.isArray(current)) { | |
throw new Error(`Cannot access index ${key} of non-array`); | |
} | |
// Create a copy of the array to maintain immutability | |
current = [...current]; | |
} else { | |
// For object properties, create a new object if it doesn't exist | |
if (current[key] === undefined) { | |
// Check if the next key is a number, if so create an array, otherwise an object | |
const nextKey = pathArray[i + 1]; | |
current[key] = typeof nextKey === 'number' ? [] : {}; | |
} else { | |
// Create a shallow copy to maintain immutability | |
current[key] = Array.isArray(current[key]) ? [...current[key]] : { ...current[key] }; | |
} | |
} | |
// Move to the next level | |
current = current[key]; | |
} | |
// Set the value at the final path segment | |
const finalKey = pathArray[pathArray.length - 1]; | |
current[finalKey] = value; | |
return result; | |
} | |
/** | |
* Custom hook for managing a complex state object with string path updates | |
* @param initialState The initial state object | |
* @returns [state, setValue] tuple | |
*/ | |
export function useNestedState<T>(initialState: T): [T, (value: any, path?: string) => void] { | |
const [state, setState] = React.useState<T>(initialState); | |
const setValue = React.useCallback((value: any, path?: string) => { | |
if (path === undefined) { | |
setState(value); | |
return; | |
} | |
setState(prevState => setNestedValue(prevState, value, path)); | |
}, []); | |
return [state, setValue]; | |
} | |