facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
227.12k stars 46.3k forks source link

How to access impure values during render (initially labeled as compiler bug) #29765

Closed jithyan closed 3 months ago

jithyan commented 3 months ago

What kind of issue is this?

Link to repro

https://playground.react.dev/#N4Igzg9grgTgxgUxALhAMygOzgFwJYSYAEAggA5kAUAlEcADrFFyFg5EDaEOZANEWAQ4A8jwC6RALxEoggMo4AhjgSV6IddQDcjRkSIwhsYmqb6APAD49+2+YAmeAG7MANorBgAcooC2CSXU4RRh7dWszWwsAIygcHEIbKKjCAGFXPDgAa0lgGilLASFRKkoAWWUACwA6GEVMewhffIAqIgBGAAZO6mqEgDE8AA8Ee0oevogFGDxMAHMaagBfJOSI5OSAcSEiYQAVAAVVqPMAelj4wnXks8cna5O5SogAdyIXyoRMXIAhCAhXAh6pRuGRlg8bncIRtzJUAEyWfYHZB0UFLM7w6F2U5Q452XGRGFkLEbfpCOCfey7Q5ESjANG0ZR0TAIN4AEWUql6CQAMhBgoC9nh-NNZgtlniTqdiZKiLdnFizk9XhCztdtIwVphGAghmQIDB2BhsPhCERlS86TYPl9eDYKXhXPZDJg7UwEK55M83tJMFBXK43UsUQwmDbMCjov9AfUdEwHU6XSiAEpA3DVVOKXBeCD2BBx-Qer2vAD8KIAUnIABrVACigP8mHYAB8iH6A3GltQU2mcBnezm80RW+3XHQbIYcMZ3p9iCXmJVHc6vkQUUWEBbOyAlkA

Repro steps

Using compiler version 0.0.0-experimental-938cd9a-20240601 in a Tauri/Vite React app.

I have a little app to parse OTPs from an e-mail, which would show the timestamp of when the OTP was retrieved. Adding the compiler resulted in only the timestamp never changing while the OTP changed (minimal code to reproduce in the link).

image

I isolated the issue to this helper component:

function Show({
  when,
  children,
  elseShow = null,
}: {
  when: boolean;
  children: React.ReactNode;
  elseShow?: JSX.Element | null;
}): React.ReactNode | null {
  return when ? children : elseShow;
}

Instead of using the Show component, if I just directly used a ternary it works in the compiler:

otp ?  (<> {/*show otp and timestamp*/} </>) : null

Am I doing something unusual in React or is this a compiler issue?

How often does this bug happen?

Every time

What version of React are you using?

19.0.0-rc.0

josephsavona commented 3 months ago

Thanks for posting. One of the key rules of React is that components and hooks must be pure. That is, they should be pure functions of their props, state, and context. Functions such as Math.random() and new Date() are impure, and cause render results to change even though props, state, and context haven't.

React Compiler builds on this rule to optimize code. In this case, it's fine to access Math.random() or new Date() in an event handler or effect. But to give the best advice it would help to understand why you need to access these in render.

jithyan commented 3 months ago

Yes you're correct, the logic would be more accurate if re-written to calculate the timestamp on retrieval rather than on render. I assumed the compiler would have bailed on memoization if I violated a rule, but that was a mistake on my part.