|
import React from 'react'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function setNestedValue<T, V>(obj: T, value: V, path?: string): T { |
|
|
|
const result = { ...obj }; |
|
|
|
|
|
if (!path) { |
|
path = ''; |
|
} |
|
|
|
|
|
const pathArray = path.split('.').flatMap(segment => { |
|
|
|
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 [propName, ...(indices || [])]; |
|
} |
|
return segment; |
|
}); |
|
|
|
|
|
let current: any = result; |
|
for (let i = 0; i < pathArray.length - 1; i++) { |
|
const key = pathArray[i]; |
|
|
|
|
|
if (typeof key === 'number') { |
|
if (!Array.isArray(current)) { |
|
throw new Error(`Cannot access index ${key} of non-array`); |
|
} |
|
|
|
current = [...current]; |
|
} else { |
|
|
|
if (current[key] === undefined) { |
|
|
|
const nextKey = pathArray[i + 1]; |
|
current[key] = typeof nextKey === 'number' ? [] : {}; |
|
} else { |
|
|
|
current[key] = Array.isArray(current[key]) ? [...current[key]] : { ...current[key] }; |
|
} |
|
} |
|
|
|
|
|
current = current[key]; |
|
} |
|
|
|
|
|
const finalKey = pathArray[pathArray.length - 1]; |
|
current[finalKey] = value; |
|
|
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
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]; |
|
} |
|
|