Skip to content

v2.0.1 refactor: react-doctor cleanup — derive state, cache Intl, slim exports - #31

Merged
baymac merged 1 commit into
mainfrom
baymac/react-doctor-lib
May 31, 2026
Merged

v2.0.1 refactor: react-doctor cleanup — derive state, cache Intl, slim exports#31
baymac merged 1 commit into
mainfrom
baymac/react-doctor-lib

Conversation

@baymac

@baymac baymac commented May 31, 2026

Copy link
Copy Markdown
Owner

Summary

A compliance pass against the react-doctor lint rules, plus the perf and
cleanup wins they surface. Behavior-preserving — no public-API change, no
visual change. The package entry (src/index.ts) still exports only
SchedulerRoot + types; every export removed here was an internal
cross-module helper never reachable from the package.

Derived state over state-syncing effects

  • Hour / Minute / DayOfMonth range pickers compute their cross-disabled options
    during render instead of mirroring them into useState via effects, so a
    start/end change reflects in the same render rather than a render later.
  • Scheduler derives periodIndex from the selected period during render.
  • NextRuns resets cursor/selection during render via a ref guard (the
    React-recommended reset-on-prop-change pattern) instead of an effect.
  • Month / Week drop their pointless useState option mirrors.

Performance

  • Intl.DateTimeFormat / Intl.RelativeTimeFormat formatters in nextRuns.ts
    and NextRuns.tsx are cached by locale + options, so identical formatters
    are reused across renders and per-run loops instead of rebuilt each call
    (some were constructed inside per-run loops).

Cleanup

  • Extract range() into src/range.ts (no imports) to break the
    constants.tsutils.ts cycle.
  • Remove dead code (getDayOfMonthIndex, defaultHourOptionsHr,
    atEveryOptionsNonAdmin) and trim the internal export surface.
  • CustomSelect: hoist getSizeConfig to module scope; pass an explicit
    Chip key instead of letting one arrive via prop spread (React forbids that).
  • Drop the unused vitest-browser-react dev dependency.
  • Annotate the genuine two-way-binding / async / external-store effects with
    react-doctor-disable-next-line so the legitimate cases stay documented.

Test Coverage

Behavior-preserving refactor — covered by the existing suite (187 tests across
4 files, all passing). The only genuinely new code, range(), moved to
src/range.ts and is exercised directly by src/utils.test.ts. The
effect→derived-state conversions produce the same option/disabled output the
existing tests already assert.

Pre-Landing Review

No blocking issues. One informational note: in src/fields/Minute.tsx,
baseTimes is recreated each render and listed in the two useMemo dep arrays,
so those memos never hit cache (correct output, just no perf win — unlike
Hour/DayOfMonth which key off module-level constants). Left as-is; harmless.

Design Review

No visual change. getSizeConfig values are identical to before (only hoisted);
no layout or styling changes.

TODOS

No TODO items completed — TODOS.md tracks the segmented-controls work on a
different branch (PR #27/#28), unrelated to this cleanup.

Test plan

  • yarn test — 187 passed (4 files)
  • yarn build — esm + cjs + .d.ts generation succeed
  • yarn lint — clean (biome)

🤖 Generated with Claude Code

…ts (v2.0.1)

Compliance pass against the react-doctor lint rules, plus the perf/cleanup
wins they surface. Behavior-preserving: all 187 tests pass, build + .d.ts
generation succeed, lint clean.

- Replace state-syncing effects with derived state: the hour/minute/
  day-of-month range pickers compute cross-disabled options during render;
  Scheduler derives periodIndex; NextRuns resets cursor/selection during
  render via a ref guard; Month/Week/Scheduler drop useState mirrors.
- Cache Intl.DateTimeFormat / RelativeTimeFormat by locale+options in
  nextRuns.ts and NextRuns.tsx so formatters are reused, not rebuilt per
  call (some ran inside per-run loops).
- Extract range() into src/range.ts (no imports) to break the constants<->
  utils cycle; trim dead code (getDayOfMonthIndex, defaultHourOptionsHr,
  atEveryOptionsNonAdmin) and reduce the internal export surface.
- CustomSelect: hoist getSizeConfig to module scope and pass an explicit
  Chip key instead of letting one arrive via prop spread.
- Annotate the genuine two-way-binding / async / external-store effects with
  react-doctor-disable-next-line so the legitimate cases stay documented.
- Drop the unused vitest-browser-react dev dependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@baymac
baymac merged commit b83f2f1 into main May 31, 2026
2 checks passed
@baymac
baymac deleted the baymac/react-doctor-lib branch May 31, 2026 07:38
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