Skip to content

refactor(core): split useForm internals by concern#278

Merged
RtlZeroMemory merged 1 commit intomainfrom
codex/split-useform
Mar 17, 2026
Merged

refactor(core): split useForm internals by concern#278
RtlZeroMemory merged 1 commit intomainfrom
codex/split-useform

Conversation

@RtlZeroMemory
Copy link
Copy Markdown
Owner

@RtlZeroMemory RtlZeroMemory commented Mar 17, 2026

Summary

  • Split packages/core/src/forms/useForm.ts into internal helper modules.
  • Kept the public useForm API unchanged.
  • No intended behavior change.

Why

  • Reduce monolithic hook complexity before tackling runtime danger zones.

Validation

  • ln -s /home/k3nig/Rezi/node_modules /home/k3nig/Rezi-split-useform/node_modules
  • npm run lint
  • npm run typecheck
  • npm run build
  • node scripts/run-tests.mjs --filter "packages/core/dist/forms/__tests__/"
  • rm node_modules

Summary by CodeRabbit

Release Notes

  • New Features
    • Dynamic array field management supporting append, remove, and move operations for flexible form structures
    • Multi-step form wizard functionality enabling step-by-step navigation with built-in validation
    • Field-level disable and read-only controls with integrated validation
    • Robust sync and async validation handling with improved error tracking

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 17, 2026

📝 Walkthrough

Walkthrough

The PR refactors form management by extracting monolithic logic from useForm.ts into specialized internal modules for array fields, bindings, state, submission, and wizards. The public API remains unchanged while internal complexity is distributed across focused modules.

Changes

Cohort / File(s) Summary
Form State Management Utilities
packages/core/src/forms/internal/state.ts
Comprehensive state initialization, dirty tracking, error handling, field override resolution, and factory functions for state accessors and flag actions (setDisabled, setReadOnly, etc.). Provides foundational utilities for form state mutations and validation filtering.
Field Array Operations
packages/core/src/forms/internal/arrayState.ts
Array field value extraction and manipulation via useFieldArray hook-like API. Handles append, remove, move operations with immutable state updates, field key management, dirty/touched tracking, and validation integration for dynamic array fields.
Field Binding and Event Handling
packages/core/src/forms/internal/bindings.ts
Factory for creating field input bindings with validation hooks. Produces bind(), field(), handleChange(), handleBlur() handlers with conditional sync/async validation, dirty state computation, and UI field rendering with error display.
Submit and Reset Actions
packages/core/src/forms/internal/submit.ts
Orchestrates form submission and reset flows. Handles wizard step transitions, sync/async validation, touched field marking, submit lifecycle (onSubmit, onSubmitError), and optional resetOnSubmit behavior.
Wizard Step Management
packages/core/src/forms/internal/wizard.ts
Multi-step form navigation and validation. Provides step bounds handling, step field resolution, error merging, transition validation, and action creators (nextStep, previousStep, goToStep) with async gating and error state management.
Development Utilities
packages/core/src/forms/internal/dev.ts
Dev-mode warning and error formatting utilities. Exports DEV_MODE flag, warnDev() for conditional console warnings, and formatErrorForDev() for consistent error message formatting.
Hook Refactorization
packages/core/src/forms/useForm.ts
Refactored to delegate to new internal modules for state management, bindings, submission, and wizard logic. Replaced ~1400 lines of inline utilities with modular factory function calls while preserving public API surface.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

The refactoring spans five new substantial modules with intricate state management, validation orchestration across sync/async paths, wizard step transitions, and field array operations. Multiple interdependent flows require verification of correctness, state immutability, and error propagation patterns.

Possibly related PRs

  • PR #267: Modifies overlapping form internals including wizard gating, same-turn state reads, and submit/edit locking constraints that interact with this refactored architecture.

Poem

🐰 Hop along through forms so neat,
Where state and bindings gently meet,
Arrays dance and wizards guide,
Fields now modular, far and wide,
Refactored joy in every byte! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: refactoring the useForm hook to split its internals by concern into separate modules, which aligns with the comprehensive restructuring across multiple internal files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/split-useform
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can approve the review once all CodeRabbit's comments are resolved.

Enable the reviews.request_changes_workflow setting to automatically approve the review once all CodeRabbit's comments are resolved.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/forms/internal/arrayState.ts`:
- Around line 168-181: The array branch logic (in append() and move()) is
reusing prev.dirty[fieldKey] slices instead of recomputing dirtiness for each
index against the initial values; update the code that currently builds
nextDirty (the blocks around nextValuesArray, previousDirty, appendedDirty) to
call computeFieldDirty(fieldKey, nextValuesArray,
options.initialValuesRef.current) (or equivalent) so dirty flags are derived
from nextValuesArray and initialValuesRef.current rather than splicing
prev.dirty[fieldKey]; ensure the move() and append() sites (and the similar
blocks at the noted ranges) use computeFieldDirty instead of reshaping
previousDirty so isDirty stays correct after move/remove/append.
- Around line 187-190: When appending/removing/moving array elements the code
currently calls normalizeErrorArray(prev.errors[fieldKey], ...) which converts
scalar (string) array-level errors into undefined[] and clears messages when
validateOnChange is disabled; instead, add the same guard used in remove() and
move(): if validateOnChange is false and prev.errors[fieldKey] is a scalar (not
an array), preserve prev.errors[fieldKey] as-is for nextErrors[fieldKey];
otherwise call normalizeErrorArray and update the array (e.g., for the append
branch that builds nextErrors = {...prev.errors, [fieldKey]: [...currentErrors,
undefined]}). Apply this same conditional logic to the other affected blocks
(the similar append/remove/move handling at the noted sections).

In `@packages/core/src/forms/internal/submit.ts`:
- Around line 32-44: The reset can race with in-flight async submit/validation;
update createSubmitAction to capture a unique "attempt" token (e.g., read a new
local const attempt = options.attemptRef.current or increment a local counter)
before each async flow (validation and onSubmit), and after every await
immediately check that the current attempt/token still matches and bail out if
not; ensure all post-await branches that set errors, submitError, call
finishSuccessfulSubmit(), or call updateFormState are skipped when the token is
stale, and keep createResetAction unchanged except that it should update the
shared attempt/token (so reset invalidates ongoing attempts).

In `@packages/core/src/forms/internal/wizard.ts`:
- Around line 172-192: runWizardStepValidation is incorrectly merging stale
errors from options.source.errors for the same stepFields back into the current
step validation result; remove the merge that re-inserts
pickValidationFields(options.filterDisabledValidationErrors(options.source.errors,
options.source), stepFields) so that current-step validation only returns
syncStepErrors and the current step's customStepErrors (and do not reapply old
source errors for those same fields). If you still need to preserve errors for
other steps, keep merging options.source.errors only for fields outside
stepFields (i.e., exclude stepFields before merging) and use the existing
helpers mergeValidationErrors and pickValidationFields to implement that
exclusion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c3dbb22e-910e-4279-97eb-0f6556cdbc9e

📥 Commits

Reviewing files that changed from the base of the PR and between 1757698 and 487a956.

📒 Files selected for processing (7)
  • packages/core/src/forms/internal/arrayState.ts
  • packages/core/src/forms/internal/bindings.ts
  • packages/core/src/forms/internal/dev.ts
  • packages/core/src/forms/internal/state.ts
  • packages/core/src/forms/internal/submit.ts
  • packages/core/src/forms/internal/wizard.ts
  • packages/core/src/forms/useForm.ts

@RtlZeroMemory RtlZeroMemory merged commit a76dd79 into main Mar 17, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant