7.9 KiB
7.9 KiB
Migration Guide: useAbacusState → useAbacusPlaceStates
Overview
The useAbacusState hook has been deprecated in favor of the new useAbacusPlaceStates hook. This migration is part of a larger architectural improvement to eliminate array-based column indexing in favor of native place-value semantics.
Why Migrate?
Problems with useAbacusState (deprecated)
- ❌ Uses array indices for columns (0=leftmost, requires totalColumns)
- ❌ Requires threading
totalColumnsthrough component tree - ❌ Index math creates confusion:
columnIndex = totalColumns - 1 - placeValue - ❌ Prone to off-by-one errors
- ❌ No support for BigInt (large numbers >15 digits)
Benefits of useAbacusPlaceStates (new)
- ✅ Uses place values directly (0=ones, 1=tens, 2=hundreds)
- ✅ Native semantic meaning, no index conversion needed
- ✅ Cleaner architecture with
Map<PlaceValue, State> - ✅ Supports both
numberandBigIntfor large values - ✅ Type-safe with
ValidPlaceValues(0-9) - ✅ No totalColumns threading required
Migration Steps
1. Update Hook Usage
Before (deprecated):
import { useAbacusState } from "@soroban/abacus-react";
function MyComponent() {
const {
value,
setValue,
columnStates, // Array of column states
getColumnState,
setColumnState,
toggleBead,
} = useAbacusState(123, 5); // totalColumns=5
// Need to calculate indices
const onesColumnIndex = 4; // rightmost
const tensColumnIndex = 3; // second from right
return <AbacusReact value={value} columns={5} />;
}
After (new):
import { useAbacusPlaceStates } from "@soroban/abacus-react";
function MyComponent() {
const {
value,
setValue,
placeStates, // Map<PlaceValue, PlaceState>
getPlaceState,
setPlaceState,
toggleBeadAtPlace,
} = useAbacusPlaceStates(123, 4); // maxPlaceValue=4 (0-4 = 5 columns)
// Direct place value access - no index math!
const onesState = getPlaceState(0);
const tensState = getPlaceState(1);
return <AbacusReact value={value} columns={5} />;
}
2. Update State Access Patterns
Before (array indexing):
// Get state for tens column (need to know position in array)
const tensIndex = columnStates.length - 2; // second from right
const tensState = columnStates[tensIndex];
After (place value):
// Get state for tens place - no calculation needed!
const tensState = getPlaceState(1); // 1 = tens place
3. Update State Manipulation
Before:
// Toggle bead in ones column (need BeadConfig with column index)
toggleBead({
type: "earth",
value: 1,
active: false,
position: 2,
placeValue: 0, // This was confusing - had place value BUT operated on column index
});
After:
// Toggle bead at ones place - clean and semantic
toggleBeadAtPlace({
type: "earth",
value: 1,
active: false,
position: 2,
placeValue: 0, // Now actually used as place value!
});
4. Update Iteration Logic
Before (array iteration):
columnStates.forEach((state, columnIndex) => {
const placeValue = columnStates.length - 1 - columnIndex; // Manual conversion
console.log(`Column ${columnIndex} (place ${placeValue}):`, state);
});
After (Map iteration):
placeStates.forEach((state, placeValue) => {
console.log(`Place ${placeValue}:`, state); // Direct access, no conversion!
});
API Comparison
useAbacusState (deprecated)
function useAbacusState(
initialValue?: number,
targetColumns?: number,
): {
value: number;
setValue: (newValue: number) => void;
columnStates: ColumnState[]; // Array
getColumnState: (columnIndex: number) => ColumnState;
setColumnState: (columnIndex: number, state: ColumnState) => void;
toggleBead: (bead: BeadConfig) => void;
};
useAbacusPlaceStates (new)
function useAbacusPlaceStates(
controlledValue?: number | bigint,
maxPlaceValue?: ValidPlaceValues,
): {
value: number | bigint;
setValue: (newValue: number | bigint) => void;
placeStates: PlaceStatesMap; // Map
getPlaceState: (place: ValidPlaceValues) => PlaceState;
setPlaceState: (place: ValidPlaceValues, state: PlaceState) => void;
toggleBeadAtPlace: (bead: BeadConfig) => void;
};
Complete Example
Before: Array-based (deprecated)
import { useState } from "react";
import { useAbacusState, AbacusReact } from "@soroban/abacus-react";
function DeprecatedExample() {
const { value, setValue, columnStates } = useAbacusState(0, 3);
const handleAddTen = () => {
// Need to know array position of tens column
const totalColumns = columnStates.length;
const tensColumnIndex = totalColumns - 2; // Complex!
const current = columnStates[tensColumnIndex];
// Increment tens digit
const currentTensValue =
(current.heavenActive ? 5 : 0) + current.earthActive;
const newTensValue = (currentTensValue + 1) % 10;
setValue(value + 10);
};
return (
<div>
<AbacusReact value={value} columns={3} interactive />
<button onClick={handleAddTen}>Add 10</button>
</div>
);
}
After: Place-value based (new)
import { useState } from "react";
import { useAbacusPlaceStates, AbacusReact } from "@soroban/abacus-react";
function NewExample() {
const { value, setValue, getPlaceState } = useAbacusPlaceStates(0, 2);
const handleAddTen = () => {
// Direct access to tens place - simple!
const tensState = getPlaceState(1); // 1 = tens
// Increment tens digit
const currentTensValue =
(tensState.heavenActive ? 5 : 0) + tensState.earthActive;
const newTensValue = (currentTensValue + 1) % 10;
if (typeof value === "number") {
setValue(value + 10);
} else {
setValue(value + 10n);
}
};
return (
<div>
<AbacusReact value={value} columns={3} interactive />
<button onClick={handleAddTen}>Add 10</button>
</div>
);
}
BigInt Support (New Feature)
The new hook supports BigInt for numbers exceeding JavaScript's safe integer limit:
const { value, setValue } = useAbacusPlaceStates(
123456789012345678901234567890n, // BigInt!
29, // 30 digits (place values 0-29)
);
console.log(typeof value); // "bigint"
Type Safety Improvements
The new hook uses branded types and strict typing:
import type {
ValidPlaceValues, // 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
PlaceState,
PlaceStatesMap,
} from "@soroban/abacus-react";
// Type-safe place value access
const onesState: PlaceState = getPlaceState(0);
const tensState: PlaceState = getPlaceState(1);
// Compile-time error for invalid place values
const invalidState = getPlaceState(15); // Error if maxPlaceValue < 15
Timeline
- Current: Both hooks available,
useAbacusStatemarked@deprecated - Next major version:
useAbacusStatewill be removed - Recommendation: Migrate as soon as possible
Getting Help
If you encounter issues during migration:
- Check the README.md for updated examples
- Review Storybook stories for usage patterns
- Open an issue at https://github.com/anthropics/claude-code/issues
Summary
| Feature | useAbacusState (old) | useAbacusPlaceStates (new) |
|---|---|---|
| Architecture | Array-based columns | Map-based place values |
| Index math | Required | Not needed |
| Semantic meaning | Indirect | Direct |
| BigInt support | ❌ No | ✅ Yes |
| Type safety | Basic | Enhanced |
| Column threading | Required | Not required |
| Status | ⚠️ Deprecated | ✅ Recommended |
Bottom line: The new hook eliminates complexity and makes your code more maintainable. Migration is straightforward - primarily renaming and removing index calculations.