React useState Integration
Relevant source files
The following files were used as context for generating this wiki page:
data-path provides a type-safe mechanism for managing deeply nested state within React's useState hook. By utilizing Proxy-based lambda expressions, the library allows developers to define paths to specific state properties with full IDE autocomplete and compile-time validation.
The integration focuses on solving the complexity of manual object spreading in React functional updates. When a state update is required, data-path performs an immutable write that returns a structural clone of the state, ensuring that React's reactivity system detects the change without the developer needing to manually manage nested rest/spread operations.
Sources: README.md:126-128, skills/data-path/SKILL.md:46-49
Core Integration Logic
The primary interface for React integration is the path.set(data, value) method. In a useState context, this method is typically used within a functional updater to transform the previous state into a new, immutably updated version.
Functional Updates with Immutable Paths
When path.set() is called, it does not mutate the original state object. Instead, it creates new references only along the specific branch of the object tree that has changed, while preserving references for all unchanged branches through structural sharing.
Sources: src/impl/path-impl.ts:98-132, src/tests/data-access.spec.ts:88-115
This diagram illustrates how path.set generates a new state object for React while optimizing memory and performance through structural sharing.
Sources: src/impl/path-impl.ts:98-115, src/tests/data-access.spec.ts:108-115
Key API Elements for React
The following table summarizes the elements most relevant when integrating with React state:
| Element | Description | Usage in React |
|---|---|---|
path<T>(p => p.a.b) | Creates a type-safe path | Define state targets at module scope or inside components. |
.set(data, value) | Immutable write operation | Used inside setState(prev => path.set(prev, val)). |
.$ | String representation | Useful for the name or key props in form elements. |
.get(data) | Type-safe read | Read values from state without "cannot read property of undefined" errors. |
Sources: README.md:126-140, skills/data-path/SKILL.md:95-103
Implementation Details
Structural Sharing and Immutability
The PathImpl.set method implementation ensures that only the necessary parts of the state tree are duplicated. If a path points to settings.profile.theme, the update will create a new settings object and a new profile object, but other top-level properties of the state remain referentially identical to the previous version.
Sources: src/impl/path-impl.ts:104-124, src/tests/data-access.spec.ts:108-115
The sequence shows the transition from a component trigger to the generation of a new state reference via the path implementation.
Sources: src/impl/path-impl.ts:98-132, README.md:144-154
Handling Dynamic Indices
In React lists (e.g., mapping over an array of items), paths can capture runtime variables like loop indices. Because the lambda is executed once during the path() call, the index is baked into the resulting path object.
// Inside a React component map
const itemPath = path<AppState>(p => p.items[index].title);
const updateTitle = (newTitle: string) => {
setState(prev => itemPath.set(prev, newTitle));
};
Sources: README.md:167-180, src/tests/path.spec.ts:21-25
Usage Workflow
The recommended workflow for integrating data-path with useState involves:
- Path Definition: Define paths outside the component if they are static, or inside the component if they depend on props or indices.
- State Initialization: Initialize React
useStatewith the root object type. - Update Execution: Use the functional update pattern
setState(prev => ...)to ensure updates are based on the latest state and leveragepath.set()for the transformation.
Sources: skills/data-path/SKILL.md:46-51, README.md:255-271
Example Integration
import { useState } from "react";
import { path } from "data-path";
type AppState = { settings: { profile: { theme: string } } };
const themePath = path<AppState>(p => p.settings.profile.theme);
function ThemeToggle() {
const [state, setState] = useState<AppState>({
settings: { profile: { theme: "light" } },
});
const setTheme = (theme: string) => {
// Returns a structural clone to React
setState(prev => themePath.set(prev, theme));
};
return <button onClick={() => setTheme("dark")}>{state.settings.profile.theme}</button>;
}
Sources: README.md:258-271
Conclusion
Integrating data-path with React useState eliminates the boilerplate and runtime risks associated with updating deep state. By providing a type-safe set method that automatically handles structural cloning, the library ensures that components remain performant and maintainable while strictly adhering to TypeScript's type system.