Skip to content

fix(children): close iterators when React.Children.map callback throws - #37463

Open
mturac wants to merge 1 commit into
react:mainfrom
mturac:fix/issue-37427
Open

fix(children): close iterators when React.Children.map callback throws#37463
mturac wants to merge 1 commit into
react:mainfrom
mturac:fix/issue-37427

Conversation

@mturac

@mturac mturac commented Aug 30, 2026

Copy link
Copy Markdown

Summary

Fixes #37427React.Children.map (and forEach, count, toArray) manually iterates via iterator.next() but never calls iterator.return() when the map callback or recursive traversal throws.

Root cause: mapIntoArray in ReactChildren.js consumes the iterator in a while loop. If the user-supplied callback or the recursive mapIntoArray call throws, the function exits without signaling the iterator. Per the iterator protocol, a consumer that stops early must call return() so the producer can run cleanup (finally blocks in generators, resource release in custom iterables).

Fix: Wrap the iteration loop in try/finally. A tracking flag (iteratorDone) distinguishes normal completion from abrupt exit. The finally block calls return() only when the iterator did not run to completion, and swallows any error from return() itself to preserve the original error.

Changed files

  • packages/react/src/ReactChildren.js — try/finally around iterator consumption in mapIntoArray
  • packages/react/src/__tests__/ReactChildren-test.js — regression test: custom iterable with a return() spy, map callback throws on second element, verifies return() was called exactly once

Test plan

  • New test: iterator return() spy called when map callback throws — passed
  • Full ReactChildren-test.js suite — 43/43 passed
@meta-cla meta-cla Bot added the CLA Signed label Aug 30, 2026
@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown

A size report will appear here when the build finishes.

Generated by sizebot against 11aa633

React.Children manually iterates via next() but never calls return()
when the map callback or recursive traversal throws. This leaves
generator cleanup and custom iterable resource release pending.

Wrap the iteration loop in try/finally so return() is called on any
abrupt exit while preserving the original error.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

1 participant