From 204fc620ea8d35573dfc44ddb601cfde2192e138 Mon Sep 17 00:00:00 2001 From: Aniketiitk21 Date: Sun, 24 May 2026 07:52:32 +0530 Subject: [PATCH] =?UTF-8?q?[compiler]=20Don=E2=80=99t=20treat=20JSX=20prop?= =?UTF-8?q?=20callbacks=20as=20render-invoked?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/HIR/CollectHoistablePropertyLoads.ts | 16 ++--- ...null-assertion-disabled-callback.expect.md | 61 +++++++++++++++++++ ...g-non-null-assertion-disabled-callback.tsx | 12 ++++ ...maybe-mutate-context-in-callback.expect.md | 4 +- ...Context-read-context-in-callback.expect.md | 4 +- 5 files changed, 81 insertions(+), 16 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-non-null-assertion-disabled-callback.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-non-null-assertion-disabled-callback.tsx diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts index c47a41145157..8e43047603b9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectHoistablePropertyLoads.ts @@ -759,19 +759,11 @@ function getAssumedInvokedFunctions( } } else if (value.kind === 'JsxExpression') { /** - * Assume JSX attributes and children are safe to invoke + * The JSX tag is invoked during render, but props and children may be + * event handlers or opaque values that are only consumed later. */ - for (const attr of value.props) { - if (attr.kind === 'JsxSpreadAttribute') { - continue; - } - const maybeLoweredFunc = temporaries.get(attr.place.identifier.id); - if (maybeLoweredFunc != null) { - hoistableFunctions.add(maybeLoweredFunc.fn); - } - } - for (const child of value.children ?? []) { - const maybeLoweredFunc = temporaries.get(child.identifier.id); + if (value.tag.kind === 'Identifier') { + const maybeLoweredFunc = temporaries.get(value.tag.identifier.id); if (maybeLoweredFunc != null) { hoistableFunctions.add(maybeLoweredFunc.fn); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-non-null-assertion-disabled-callback.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-non-null-assertion-disabled-callback.expect.md new file mode 100644 index 000000000000..b9369989fe11 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-non-null-assertion-disabled-callback.expect.md @@ -0,0 +1,61 @@ + +## Input + +```javascript +function Component({test}: {test: null | {value: string}}) { + return ( + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{test: null}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +function Component(t0) { + const $ = _c(5); + const { test } = t0; + + const t1 = !test; + let t2; + if ($[0] !== test) { + t2 = () => console.log(test.value); + $[0] = test; + $[1] = t2; + } else { + t2 = $[1]; + } + let t3; + if ($[2] !== t1 || $[3] !== t2) { + t3 = ( + + ); + $[2] = t1; + $[3] = t2; + $[4] = t3; + } else { + t3 = $[4]; + } + return t3; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ test: null }], +}; + +``` + +### Eval output +(kind: ok) \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-non-null-assertion-disabled-callback.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-non-null-assertion-disabled-callback.tsx new file mode 100644 index 000000000000..23b91d8e2428 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-non-null-assertion-disabled-callback.tsx @@ -0,0 +1,12 @@ +function Component({test}: {test: null | {value: string}}) { + return ( + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{test: null}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useContext-maybe-mutate-context-in-callback.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useContext-maybe-mutate-context-in-callback.expect.md index 0525a0f7cdd9..117031695888 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useContext-maybe-mutate-context-in-callback.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useContext-maybe-mutate-context-in-callback.expect.md @@ -41,11 +41,11 @@ function Component(props) { const $ = _c(5); const Foo = useContext(FooContext); let t0; - if ($[0] !== Foo.current) { + if ($[0] !== Foo) { t0 = () => { mutate(Foo.current); }; - $[0] = Foo.current; + $[0] = Foo; $[1] = t0; } else { t0 = $[1]; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useContext-read-context-in-callback.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useContext-read-context-in-callback.expect.md index e805b7f400e3..09dbbc230f3b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useContext-read-context-in-callback.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useContext-read-context-in-callback.expect.md @@ -34,11 +34,11 @@ function Component(props) { const $ = _c(5); const foo = useContext(FooContext); let t0; - if ($[0] !== foo.current) { + if ($[0] !== foo) { t0 = () => { console.log(foo.current); }; - $[0] = foo.current; + $[0] = foo; $[1] = t0; } else { t0 = $[1];