[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.0Source: react/react