Skip to content

Conversation

@josephsavona
Copy link
Member

@josephsavona josephsavona commented Dec 5, 2025

note: This implements the idea discussed in https://github.com/reactwg/react/discussions/389#discussioncomment-14252280

Currently we create Impure effects for impure functions like Date.now() or Math.random(), and then throw if the effect is reachable during render. However, impurity is a property of the resulting value: if the value isn't accessed during render then it's okay: maybe you're console-logging the time while debugging (fine), or storing the impure value into a ref and only accessing it in an effect or event handler (totally ok).

This PR updates to validate that impure values are not transitively consumed during render, building on the Render effect. The validation currently uses an algorithm very similar to that of InferReactivePlaces - building a set of known impure values, starting with Impure effects as the sources and then flowing outward via data flow and mutations. If a transitively impure value reaches a Render effect, it's an error. We record both the source of the impure value and where it gets rendered to make it easier to understand and fix errors:

Error: Cannot access impure value during render

Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent).

error.invalid-impure-functions-in-render-via-render-helper.ts:10:25
   8 |     const array = makeArray(now);
   9 |     const hasDate = identity(array);
> 10 |     return <Bar hasDate={hasDate} />;
     |                          ^^^^^^^ Cannot access impure value during render
  11 |   };
  12 |   return <Foo renderItem={renderItem} />;
  13 | }

error.invalid-impure-functions-in-render-via-render-helper.ts:6:14
  4 |
  5 | function Component() {
> 6 |   const now = Date.now();
    |               ^^^^^^^^^^ `Date.now` is an impure function.
  7 |   const renderItem = () => {
  8 |     const array = makeArray(now);
  9 |     const hasDate = identity(array);

Impure values are allowed to flow into refs, meaning that we now allow useRef(Date.now()) or useRef(localFunctionThatReturnsMathDotRandom()) which would have errored previously. The next PR reuses this improved impurity tracking to validate ref access in render as well.


Stack created with Sapling. Best reviewed with ReviewStack.

@meta-cla meta-cla bot added the CLA Signed label Dec 5, 2025
@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label Dec 5, 2025
@josephsavona josephsavona force-pushed the pr35298 branch 4 times, most recently from f0e4d14 to 2cd48b2 Compare December 7, 2025 00:27
@josephsavona josephsavona force-pushed the pr35298 branch 4 times, most recently from 3ccb2f9 to 9899f74 Compare January 5, 2026 05:43
@josephsavona josephsavona marked this pull request as ready for review January 6, 2026 02:20
@josephsavona josephsavona changed the title [compiler][poc] Improve impurity/ref tracking [compiler] Improve impurity/ref tracking Jan 6, 2026
note: This implements the idea discussed in reactwg/react#389 (comment)

Currently we create `Impure` effects for impure functions like `Date.now()` or `Math.random()`, and then throw if the effect is reachable during render. However, impurity is a property of the resulting value: if the value isn't accessed during render then it's okay: maybe you're console-logging the time while debugging (fine), or storing the impure value into a ref and only accessing it in an effect or event handler (totally ok).

This PR updates to validate that impure values are not transitively consumed during render, building on the `Render` effect. The validation currently uses an algorithm very similar to that of InferReactivePlaces - building a set of known impure values, starting with `Impure` effects as the sources and then flowing outward via data flow and mutations. If a transitively impure value reaches a `Render` effect, it's an error. We record both the source of the impure value and where it gets rendered to make it easier to understand and fix errors:

```
Error: Cannot access impure value during render

Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent).

error.invalid-impure-functions-in-render-via-render-helper.ts:10:25
   8 |     const array = makeArray(now);
   9 |     const hasDate = identity(array);
> 10 |     return <Bar hasDate={hasDate} />;
     |                          ^^^^^^^ Cannot access impure value during render
  11 |   };
  12 |   return <Foo renderItem={renderItem} />;
  13 | }

error.invalid-impure-functions-in-render-via-render-helper.ts:6:14
  4 |
  5 | function Component() {
> 6 |   const now = Date.now();
    |               ^^^^^^^^^^ `Date.now` is an impure function.
  7 |   const renderItem = () => {
  8 |     const array = makeArray(now);
  9 |     const hasDate = identity(array);
```

Impure values are allowed to flow into refs, meaning that we now allow `useRef(Date.now())` or `useRef(localFunctionThatReturnsMathDotRandom())` which would have errored previously. The next PR reuses this improved impurity tracking to validate ref access in render as well.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed React Core Team Opened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants