Skip to content

fix: stale onError in memo/forwardRef on React 19.2, getConnectedDataURL() on ECharts 6.1 - #577

Merged
chensid merged 3 commits into
mainfrom
claude/blissful-gauss-wdmssv
Oct 1, 2026
Merged

chensid merged 3 commits into
mainfrom
claude/blissful-gauss-wdmssv

Conversation

@chensid

@chensid chensid commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Summary

This PR comes from re-reading upstream docs and sources (React, ECharts, Vite+/Vitest, TypeScript 7, pnpm). Two of the findings are real bugs: each one was reproduced before fixing and verified after. The PR also corrects docs and config.

1. onError goes stale inside memo() / forwardRef on React 19.2 (patch)

  • Cause: React 19.2.x only refreshes useEffectEvent callbacks on plain function-component fibers. ForwardRef and SimpleMemo fibers break without updating them (react-dom 19.2.7, commit phase).
  • Impact: the peer range is react ^19.2.0. So with memo(EChart), or a memoized component that calls useEcharts, effect-time errors kept going to the onError from the first render.
  • Fix: effect-time errors now read the latest onError from a ref that is synced in a layout effect.
    • useChartCore reuses latestRef; useResizeObserver gets its own ref.
    • Both report through a shared reportEffectError.
    • The library no longer uses useEffectEvent, so the docs no longer cite it as the reason for the 19.2 floor. The peer range itself is unchanged.
  • Tests: new tests render the hook inside memo() and forwardRef, swap onError, then make option sync, auto-resize and cleanup fail.
    • With react/react-dom temporarily at 19.2.7, both tests fail on the old code and pass with the fix.
    • The full unit project (282 tests) also passes on 19.2.7.

2. getConnectedDataURL() without options throws on ECharts 6.1.0 (patch)

  • Cause: ECharts 6.1.0 reads opts.type without defaulting opts. getDataURL does have a default.
  • Fix: the hook now passes opts ?? {}.
  • Tests: a new browser test calls getDataURL() and getConnectedDataURL() without options on a real chart.
    • Before the fix it fails with Cannot read properties of undefined (reading 'type'); after the fix it passes.

3. Docs and config corrections (no behaviour change)

  • CLAUDE.md, pnpm prune: it said minimumReleaseAgeExcludePrune "does not reliably remove matured entries". The pnpm docs define it as dropping only entries the lockfile no longer resolves; it never prunes by age, so removing matured entries by hand is the designed workflow. The pnpm-workspace.yaml comment is corrected the same way (the prune also runs on install and dedupe).
  • CLAUDE.md, Node: notes that the pre-commit vp staged needs Node 22.22.1+ on 22.x, matching CONTRIBUTING.md and the Vite+ commit-hooks guide.
  • Removed redundant config:
    • .vitest-attachments/ in .gitignore: Vitest 5 writes to .vitest/, which is already ignored.
    • clearMocks: true and both inline extends: true in vite.config.ts: these are Vitest 5 defaults, checked in the bundled sources.
    • DOM.Iterable in tsconfig.app.json: it is an empty stub in TypeScript 7.
  • Kept peerDependencyRules.allowedVersions: vp migrate rewrites it on every toolchain upgrade, so removing it would only cause churn.

Validation

  • vp check and vp exec tsc -b pass.
  • Unit tests with coverage: 282 passed, 100% on all metrics.
  • Browser project: 9 passed.
  • vp pack: attw and publint are clean; there are still two React Compiler caches.
  • vp build and size-limit pass (6.88 KB / 1.51 KB / 227 B).

🤖 Generated with Claude Code

https://claude.ai/code/session_01VAs1BuCUj8VWLQvBSaxqWM


Generated by Claude Code

claude added 3 commits October 1, 2026 14:55
React 19.2.x refreshes useEffectEvent callbacks only on plain function
component fibers; ForwardRef (11) and SimpleMemo (15) fibers `break`
without updating ref.impl (react-dom 19.2.7 commit phase). 19.3.0 fixed
it (react/react#34831) and the fix was never backported. With the
peer range at react ^19.2.0, `memo(EChart)` or a memoized component that
calls useEcharts kept routing effect-time errors to the first render's
onError.

Effect-time errors now read the latest onError from a ref synced in a
layout effect: useChartCore reuses latestRef, useResizeObserver gets its
own onErrorRef, and both report through a shared reportEffectError next
to routeImperativeError. The library no longer uses useEffectEvent, so
docs drop it as the reason for the 19.2 floor (the peer range itself is
unchanged).

New tests render the hook inside memo() and forwardRef components and
swap onError before failing option sync, auto-resize and cleanup. With
react/react-dom temporarily at 19.2.7 both fail on the old code and pass
with this change; the whole unit project (282 tests) also passes on
19.2.7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VAs1BuCUj8VWLQvBSaxqWM
ECharts 6.1.0's getConnectedDataURL dereferences `opts.type` with no
`opts = opts || {}` guard (getDataURL has one). The fix is on the
apache/echarts release branch (#21736, 2026-09-14) but not on npm, so
the documented `getConnectedDataURL()` threw a TypeError that the hook
routed to onError or rethrew.

The hook now passes `opts ?? {}`. A new browser test calls both
getDataURL() and getConnectedDataURL() without options on a real chart:
it fails with "Cannot read properties of undefined (reading 'type')"
before this change and passes after; the unit test asserts the `{}`
default and that explicit options still pass through.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VAs1BuCUj8VWLQvBSaxqWM
Docs, checked against the upstream sources:
- CLAUDE.md said minimumReleaseAgeExcludePrune "does not reliably remove
  matured entries". The pnpm docs (settings/dependency-resolution.md)
  define it as dropping only entries the freshly written lockfile no
  longer resolves; it never prunes by age. Reword so manual pruning reads
  as the designed workflow, not a workaround. The pnpm-workspace.yaml
  comment also listed only add/update/remove; it runs on install and
  dedupe too.
- CLAUDE.md's tooling line now notes that the pre-commit hook's
  `vp staged` needs Node 22.22.1+ on 22.x, as CONTRIBUTING.md and the
  Vite+ commit-hooks guide already say.

Config with no behaviour change:
- .gitignore: drop `.vitest-attachments/`; Vitest 5 writes attachments
  under `.vitest/`, which is already ignored.
- vite.config.ts: drop `clearMocks: true` and both inline `extends:
  true`; Vitest 5 defaults clearMocks to true and inline projects inherit
  the root config unless `extends` is false (both read from the bundled
  vitest sources).
- tsconfig.app.json: drop "DOM.Iterable"; in TypeScript 7 that lib file
  is an empty stub whose contents moved into "DOM".

peerDependencyRules.allowedVersions stays: `vp migrate` rewrites it on
every toolchain upgrade, so removing it would only churn.

Validation: `vp check`, `vp exec tsc -b`, unit tests with coverage (282
passed, 100%), browser project (9 passed), `vp pack` (attw + publint
clean, two React Compiler caches), `vp build`, size-limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VAs1BuCUj8VWLQvBSaxqWM
@chensid
chensid merged commit d450e87 into main Oct 1, 2026
2 checks passed
@chensid
chensid deleted the claude/blissful-gauss-wdmssv branch October 1, 2026 15:04
@codecov

codecov Bot commented Oct 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (52a5667) to head (2eb7a93).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #577   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           19        19           
  Lines          574       574           
  Branches       146       143    -3     
=========================================
  Hits           574       574           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

2 participants