Immutable State Updates
Relevant source files
The following files were used as context for generating this wiki page:
Immutable state updates are a core philosophy of the data-path library. Instead of mutating existing object structures, the library provides a mechanism to perform "structural clones" when writing data to a specific path. This ensures that the original data remains untouched, making the library highly compatible with modern reactive frameworks and state management tools like React, Zustand, and Redux.
Sources: README.md:42-45, skills/data-path/SKILL.md:46-52
Core Update Mechanism: .set()
The primary interface for performing immutable updates is the .set(data, value) method. This method accepts the root data object and the new value to be placed at the path's destination. It returns a new object representing the updated state while maintaining structural integrity.
Sources: src/types.ts:160-170, src/impl/path-impl.ts:106-110
Structural Sharing logic
The data-path implementation utilizes structural sharing to optimize performance and memory usage. When an update occurs:
- Path Recreation: New object or array references are created only for the segments explicitly defined in the path.
- Branch Preservation: All other branches and properties of the object tree that are not on the direct path to the updated value are reused from the original object.
Sources: src/tests/data-access.spec.ts:68-100, src/impl/path-impl.ts:114-144
The diagram shows the recursive logic used to traverse the object tree and create new references only where necessary. Sources: src/impl/path-impl.ts:111-146
Implementation Details
The internal setAt function handles the heavy lifting of the immutable update. It intelligently determines whether to create a new array or a new object based on the type of the next segment in the path.
Automatic Structure Creation
If intermediate properties are missing during a .set() operation, data-path automatically initializes them:
- Numeric Segments: If the next segment is a number, an empty array
[]is created. - String Segments: If the next segment is a string, an empty object
{}is created.
Sources: src/impl/path-impl.ts:125-132, src/tests/data-access.spec.ts:133-140
Handling Collections
The library supports updates within arrays. When a numeric index is provided in the path, the library creates a shallow copy of the array using the spread operator [...obj] and updates the specific index.
Sources: src/impl/path-impl.ts:117-121, src/tests/data-access.spec.ts:142-151
Bulk Updates with Template Paths
TemplatePath extends the immutable update capability to multiple matching elements using wildcards (* or **).
- Expansion: The template path is first expanded into a set of concrete paths based on the provided data.
- Sequential Updates: The library iterates through these concrete paths, applying an immutable
.set()operation for each match. - Result: A single new object is returned containing all changes.
Sources: src/impl/template-path-impl.ts:130-140, src/types.ts:257-268
This sequence demonstrates how bulk updates are processed as a series of individual immutable operations. Sources: src/impl/template-path-impl.ts:130-140
API Summary for Updates
| Method | Target | Return Type | Description |
|---|---|---|---|
path.set(data, value) | Single Property | T (Root Type) | Immutably sets a value at a specific path. |
templatePath.set(data, value) | Multiple Properties | T (Root Type) | Updates all properties matching the wildcard pattern. |
path.fn | Single Property | (data: T) => V | Returns an accessor function (getter only). |
Sources: src/types.ts:160-170, src/impl/template-path-impl.ts:130-140, README.md:95-103
Use Case: Integration with State Managers
The immutable nature of path.set() makes it ideal for functional state updates.
Zustand Example
// Sources: [README.md:230-243]
const themePath = path<StoreState>(p => p.settings.profile.theme);
const useStore = create<StoreState>(set => ({
settings: { profile: { theme: "light" } },
setTheme: newTheme => set(state => themePath.set(state, newTheme)),
}));
React useState Example
// Sources: [README.md:245-265]
const themePath = path<AppState>(p => p.settings.profile.theme);
function ThemeToggle() {
const [state, setState] = useState<AppState>(...);
const setTheme = (theme: string) => {
setState(prev => themePath.set(prev, theme));
};
}
Immutable state updates in data-path provide a type-safe, developer-friendly alternative to manual object spreading or libraries like Immer, ensuring data integrity across complex nested structures.