Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -655,33 +655,15 @@ function validateNoRefAccessInRenderImpl(
if (instr.value.operator === '!') {
const value = env.get(instr.value.value.identifier.id);
const refId =
value?.kind === 'RefValue' && value.refId != null
? value.refId
value?.kind === 'RefValue'
? (value.refId ?? nextRefId())
: null;
if (refId !== null) {
/*
* Record an error suggesting the `if (ref.current == null)` pattern,
* but also record the lvalue as a guard so that we don't emit a second
* error for the write to the ref
* Allow !ref.current patterns for ref initialization as they are
* semantically equivalent to ref.current == null checks
*/
env.set(instr.lvalue.identifier.id, {kind: 'Guard', refId});
errors.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.Refs,
reason: 'Cannot access refs during render',
description: ERROR_DESCRIPTION,
})
.withDetails({
kind: 'error',
loc: instr.value.value.loc,
message: `Cannot access ref value during render`,
})
.withDetails({
kind: 'hint',
message:
'To initialize a ref only once, check that the ref is null with the pattern `if (ref.current == null) { ref.current = ... }`',
}),
);
break;
}
}
Expand All @@ -693,18 +675,21 @@ function validateNoRefAccessInRenderImpl(
const right = env.get(instr.value.right.identifier.id);
let nullish: boolean = false;
let refId: RefId | null = null;
if (left?.kind === 'RefValue' && left.refId != null) {
refId = left.refId;
} else if (right?.kind === 'RefValue' && right.refId != null) {
refId = right.refId;

if (left?.kind === 'RefValue') {
refId = left.refId ?? nextRefId();
} else if (right?.kind === 'RefValue') {
refId = right.refId ?? nextRefId();
}

// Check for null or undefined values
if (left?.kind === 'Nullable') {
nullish = true;
} else if (right?.kind === 'Nullable') {
nullish = true;
}

// Allow ref comparisons with null/undefined as guards
if (refId !== null && nullish) {
env.set(instr.lvalue.identifier.id, {kind: 'Guard', refId});
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,7 @@ export const FIXTURE_ENTRYPOINT = {
## Error

```
Found 4 errors:

Error: Cannot access refs during render

React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).

4 | component C() {
5 | const r = useRef(null);
> 6 | const current = !r.current;
| ^^^^^^^^^ Cannot access ref value during render
7 | return <div>{current}</div>;
8 | }
9 |

To initialize a ref only once, check that the ref is null with the pattern `if (ref.current == null) { ref.current = ... }`
Found 3 errors:

Error: Cannot access refs during render

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

## Input

```javascript
import {useRef} from 'react';

function Component() {
const ref = useRef(undefined);
if (!ref.current) {
ref.current = "initialized";
}
return <div>Hello World</div>;
}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};
```

## Code

```javascript
import { c as _c } from "react/compiler-runtime";
import { useRef } from "react";

function Component() {
const $ = _c(1);
const ref = useRef(undefined);
if (!ref.current) {
ref.current = "initialized";
}
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = <div>Hello World</div>;
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};

```

### Eval output
(kind: ok) <div>Hello World</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {useRef} from 'react';

function Component() {
const ref = useRef(undefined);
if (!ref.current) {
ref.current = 'initialized';
}
return <div>Hello World</div>;
}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

## Input

```javascript
import {useRef} from 'react';

function Component() {
const ref = useRef(undefined);
if (ref.current === undefined) {
ref.current = "initialized";
}
return <div>Hello World</div>;
}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};
```

## Code

```javascript
import { c as _c } from "react/compiler-runtime";
import { useRef } from "react";

function Component() {
const $ = _c(1);
const ref = useRef(undefined);
if (ref.current === undefined) {
ref.current = "initialized";
}
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = <div>Hello World</div>;
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};

```

### Eval output
(kind: ok) <div>Hello World</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {useRef} from 'react';

function Component() {
const ref = useRef(undefined);
if (ref.current === undefined) {
ref.current = 'initialized';
}
return <div>Hello World</div>;
}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

## Input

```javascript
//@flow
import {useRef} from 'react';

component C() {
const r = useRef(null);
if (!r.current) {
r.current = 1;
}
}

export const FIXTURE_ENTRYPOINT = {
fn: C,
params: [{}],
};

```

## Code

```javascript
import { useRef } from "react";

function C() {
const r = useRef(null);
if (!r.current) {
r.current = 1;
}
}

export const FIXTURE_ENTRYPOINT = {
fn: C,
params: [{}],
};

```

### Eval output
(kind: ok)