#37585·react

[Compiler Bug]: Moving an unreachable early return changes child rendering

Author: tsotnegutiCreated Sep 11, 2026Updated Sep 15, 2026
LabelsType: BugStatus: Unconfirmed
### What kind of issue is this? - [x] React Compiler core (the JS output is incorrect, or your app works incorrectly after optimization) - [ ] babel-plugin-react-compiler (build issue installing or using the Babel plugin) - [ ] eslint-plugin-react-hooks (build issue installing or using the eslint plugin) - [ ] react-compiler-healthcheck (build issue installing or using the healthcheck script) ### Link to repro https://github.com/tsotneguti/bug-demo.git ### Repro steps ## Environment - Next.js `16.0.6` - React `19.2.0` - React DOM `19.2.0` - React Hook Form `7.68.0` - `@hookform/resolvers` `5.2.2` - React Compiler enabled ## Description I found a case where changing only the order of an unreachable early return and a `useEffect` changes whether a child component renders when React Hook Form's `formState.isValid` changes. `canAddMinors` is always `true`, so the early return is never actually taken. So this example should not be regarded as violation of Rules of React IMHO. ## Case 1 — `useEffect` BEFORE the early return ```tsx useEffect(() => { console.log("Parent effect"); }, [form.formState.isValid]); if (!canAddMinors) { return Error; } ``` When typing into the input changes: ```text isValid: false → true ``` `Step2` renders again, but `MinorForm` is **not invoked**. ## Case 2 — early return BEFORE `useEffect` ```tsx if (!canAddMinors) { return Error; } useEffect(() => { console.log("Parent effect"); }, [form.formState.isValid]); ``` With the same `isValid` change, `Step2` renders again and `MinorForm` **is invoked**. ## `use no memo` test Adding: ```tsx "use no memo"; ``` to `Step2` makes both versions behave identically (`MinorForm` invoked). This makes me suspect that the React Compiler optimization of the parent component is involved. ## Additional finding Replacing the computed `canAddMinors` value with a hardcoded value: ```tsx const canAddMinors = true; ``` also changes the behavior. With `canAddMinors` hardcoded to `true`, `MinorForm` does **not** render when `isValid` changes, regardless of whether the early return is positioned before or after the `useEffect`. ## Minimal reproduction ```tsx "use client"; import React, { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; const schema = z.object({ firstName: z.string().min(1, "Required"), }); type FormValues = z.infer; function MinorForm({ form, isValid, tick, }: { form: ReturnType>; isValid: boolean; tick: number; }) { console.log("MinorForm function"); useEffect(() => { console.log("MinorForm committed"); }); return (

MinorForm

Save ); } export default function Step2() { // "use no memo"; const form = useForm({ resolver: zodResolver(schema), mode: "onChange", }); const [showForm] = useState(true); const [tick, setTick] = useState(0); const applicantBirthDate = "2005-01-01"; const canAddMinors = (() => { if (!applicantBirthDate) { return false; } const birth = new Date(applicantBirthDate); const now = new Date(); const age = (now.getTime() - birth.getTime()) / (1000 * 60 * 60 * 24 * 365); return age >= 18; })(); // Move this useEffect above/below the early return // to reproduce the difference. if (!canAddMinors) { return Error; } useEffect(() => { console.log("Parent useEffect: isValid changed"); }, [form.formState.isValid]); console.log("Parent render", canAddMinors); return ( {showForm && } setTick((t) => t + 1)}>Force parent re-render ({tick}) ); } ``` ## Expected behavior Changing the position of an unreachable early return should not change whether `MinorForm` is invoked when `formState.isValid` changes by typing into the input or clearing it. ## Actual behavior The two source orderings produce different child-rendering behavior. Adding: ```tsx "use no memo"; ``` to `Step2` removes the difference. ### How often does this bug happen? Every time ### What version of React are you using? 19.2.0 ### What version of React Compiler are you using? 1.0.0