Skip to content

feat(react): expose Children API with dev-mode freeze for map/toArray#2376

Open
Huxpro wants to merge 2 commits intolynx-family:mainfrom
Huxpro:feat/react-children-api
Open

feat(react): expose Children API with dev-mode freeze for map/toArray#2376
Huxpro wants to merge 2 commits intolynx-family:mainfrom
Huxpro:feat/react-children-api

Conversation

@Huxpro
Copy link
Copy Markdown
Collaborator

@Huxpro Huxpro commented Mar 25, 2026

Summary

  • Expose Children from @lynx-js/react public API by wrapping preact/compat's Children with a custom compat layer
  • Object.freeze the arrays returned by Children.map and Children.toArray in __DEV__ to surface accidental mutations (push/splice/sort) that break compile-time snapshot optimizations
  • Add integration tests for Children.toArray and freeze behavior for both map and toArray

Test plan

  • Added Children.toArray test (flatten, filter null/undefined)
  • Added Children.toArray freeze test (frozen, push throws, non-mutating ops work)
  • Added Children.map freeze test (frozen, push throws, non-mutating ops work)
  • CI passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Re-introduced the Children API as a public export for managing component children.
  • Improvements

    • Child-related collection helpers now return frozen arrays in development to surface accidental mutations.
  • Tests

    • Added tests covering Children behaviors, immutability in dev, null handling, and thisArg binding in mapping.

- Wrap preact/compat Children with a custom compat layer
- Object.freeze the arrays returned by Children.map and Children.toArray
  in __DEV__ to surface accidental mutations that break snapshot optimizations
- Export Children from @lynx-js/react public API
- Add integration tests for Children.toArray, and freeze behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Huxpro Huxpro requested review from HuJean, Yradex and hzy as code owners March 25, 2026 06:21
Copilot AI review requested due to automatic review settings March 25, 2026 06:21
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 25, 2026

⚠️ No Changeset found

Latest commit: dab7f22

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 435ca651-7309-4491-85c7-ac876213d00b

📥 Commits

Reviewing files that changed from the base of the PR and between c940164 and dab7f22.

📒 Files selected for processing (2)
  • packages/react/runtime/__test__/children.test.jsx
  • packages/react/runtime/src/compat/children.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/react/runtime/test/children.test.jsx
  • packages/react/runtime/src/compat/children.ts

📝 Walkthrough

Walkthrough

Added a local Children wrapper that delegates to Preact's Children helpers, freezes arrays returned by map and toArray in development, updated exports and docs to include Children, and added tests covering Children.toArray and Children.map behaviors.

Changes

Cohort / File(s) Summary
Children API Implementation
packages/react/runtime/src/compat/children.ts, packages/react/runtime/src/index.ts
New Children module wrapping preact/compat Children helpers; map and toArray freeze returned arrays in __DEV__; index.ts now imports/export Children from local compat module instead of preact/compat.
Type Definitions & API Documentation
packages/react/types/react.docs.d.ts, packages/react/etc/react.api.md
Added Children to re-exported legacy React API symbols in types and API report.
Test Coverage
packages/react/runtime/__test__/children.test.jsx
Appended multiple tests (5 cases) verifying Children.toArray filters null/undefined, that toArray and map return frozen arrays in dev mode, map(null, ...) returns null, and map respects thisArg binding.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • hzy
  • Yradex
  • HuJean

Poem

"I nibble on code and hop through trees of keys, 🐇
I freeze little arrays so mischief can't squeeze,
I wrap Preact's children with a careful paw,
Tests clap their paws and approve what they saw,
A rabbit-approved API, snug as a breeze."

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 accurately describes the main change: exposing the Children API with dev-mode freeze behavior for map/toArray methods, which matches all modified files and test additions.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 25, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Exposes Children on @lynx-js/react’s public API by routing through a compat wrapper that freezes arrays returned from Children.map / Children.toArray in __DEV__, plus adds integration tests and updates the generated API/type surface.

Changes:

  • Export Children from the package entry and type/docs surfaces.
  • Introduce runtime/src/compat/children.ts wrapper that freezes map/toArray results in dev.
  • Add integration tests validating toArray behavior and dev-mode freezing for map/toArray.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/react/types/react.docs.d.ts Adds Children to the documented/type export list.
packages/react/runtime/src/index.ts Switches Children export to come from the new compat wrapper.
packages/react/runtime/src/compat/children.ts Implements the dev-freeze Children wrapper around preact/compat.
packages/react/runtime/test/children.test.jsx Adds tests for Children.toArray and dev-freeze behavior.
packages/react/etc/react.api.md Updates API Extractor report to include Children.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +31 to +33
map(children: any, fn: any): any[] {
const arr = PreactChildren.map(children, fn);
if (__DEV__) {
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

Children.map in preact/compat returns null when children is null/undefined, and also accepts an optional third thisArg/context parameter. The wrapper currently always calls Object.freeze(arr) (which will throw when arr is null) and drops the third parameter, changing behavior vs React/Preact. Guard the freeze with an array check (or arr != null) and forward the optional context argument; also update the wrapper’s return type to allow null (e.g., use ReturnType<typeof PreactChildren.map>).

Suggested change
map(children: any, fn: any): any[] {
const arr = PreactChildren.map(children, fn);
if (__DEV__) {
map(children: any, fn: any, thisArg?: any): ReturnType<typeof PreactChildren.map> {
const arr = PreactChildren.map(children, fn, thisArg);
if (__DEV__ && arr != null) {

Copilot uses AI. Check for mistakes.
// Non-mutating operations should still work
expect([...results]).toHaveLength(2);
expect(results.filter(() => true)).toHaveLength(2);
});
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

The new dev-freeze tests cover mutation attempts, but there’s no regression test for Children.map(null, fn) (should return null and not throw) or for Children.map(children, fn, thisArg) preserving the this binding. Adding these cases would catch the wrapper-specific behavioral changes introduced here.

Suggested change
});
});
it('Children.map returns null when children is null', function() {
const result = Children.map(null, (child) => child);
expect(result).toBeNull();
});
it('Children.map respects thisArg binding', function() {
const children = [<view key="a" />, <view key="b" />];
const context = { multiplier: 2 };
const seenThis = [];
const results = Children.map(
children,
function(child, index) {
seenThis.push(this);
return index * this.multiplier;
},
context,
);
expect(results).toEqual([0, 2]);
expect(seenThis.every((ctx) => ctx === context)).toBe(true);
});

Copilot uses AI. Check for mistakes.
only: typeof PreactChildren.only;
toArray: (children: ComponentChild | ComponentChild[]) => readonly any[];
} = {
forEach: PreactChildren.forEach,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

should keep same with map?

@relativeci
Copy link
Copy Markdown

relativeci bot commented Mar 25, 2026

Web Explorer

#8395 Bundle Size — 724.73KiB (0%).

c940164(current) vs fcda36a main#8391(baseline)

Bundle metrics  Change 1 change
                 Current
#8395
     Baseline
#8391
No change  Initial JS 41.99KiB 41.99KiB
No change  Initial CSS 1.99KiB 1.99KiB
Change  Cache Invalidation 0% 9.64%
No change  Chunks 8 8
No change  Assets 10 10
Change  Modules 150(+0.67%) 149
No change  Duplicate Modules 11 11
No change  Duplicate Code 34.75% 34.75%
No change  Packages 3 3
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#8395
     Baseline
#8391
No change  Other 382.38KiB 382.38KiB
No change  JS 340.36KiB 340.36KiB
No change  CSS 1.99KiB 1.99KiB

Bundle analysis reportBranch Huxpro:feat/react-children-apiProject dashboard


Generated by RelativeCIDocumentationReport issue

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 25, 2026

Merging this PR will improve performance by ×4.3

⚡ 1 improved benchmark
✅ 62 untouched benchmarks
⏩ 21 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
basic-performance-large-css 67.9 ms 15.8 ms ×4.3

Comparing Huxpro:feat/react-children-api (c940164) with main (fcda36a)

Open in CodSpeed

Footnotes

  1. 21 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

…, wrap forEach

- Guard Object.freeze with null check (preact returns null for null children)
- Forward thisArg to map/forEach via fn.bind(thisArg)
- Wrap forEach for consistency (correct void return type, thisArg support)
- Add tests for Children.map(null) and thisArg binding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Huxpro
Copy link
Copy Markdown
Collaborator Author

Huxpro commented Mar 27, 2026

@HuJean Addressed all review comments in dab7f22:

  1. Null guard for Children.map — added arr != null check before Object.freeze (preact returns null for null children)
  2. Forward thisArg — both map and forEach now support thisArg via fn.bind(thisArg)
  3. Wrapped forEach for consistency — same thisArg forwarding as map, with correct void return type
  4. Added testsChildren.map(null, fn) returns null, Children.map(children, fn, thisArg) preserves binding

PTAL, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants