mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Separate flag for ref access violation within function expressions
Adds a separate compiler flag for enabling the incomplete validation of ref access within function expressions. Unlike the previous PR for set-state-in-render validation, ref access in render can be okay in some circumstances so i'm leaving this off by default. The point of splitting this up is that our linting will be able to enable the rule without risk of false positives.
This commit is contained in:
@@ -122,6 +122,14 @@ const EnvironmentConfigSchema = z.object({
|
||||
// Validate that ref values (`ref.current`) are not accessed during render.
|
||||
validateRefAccessDuringRender: z.boolean().default(false),
|
||||
|
||||
/**
|
||||
* Extension of validateRefAccessDuringRender that validates that refs are not accessed during
|
||||
* render indirectly by calling function expressions which access the ref.
|
||||
*
|
||||
* This validation has known issues and is not yet recommended
|
||||
*/
|
||||
validateRefAccessDuringRenderFunctionExpressions: z.boolean().default(false),
|
||||
|
||||
/*
|
||||
* Validate that mutable lambdas are not passed where a frozen value is expected, since mutable
|
||||
* lambdas cannot be frozen. The only mutation allowed inside a frozen lambda is of ref values.
|
||||
|
||||
+13
-11
@@ -63,17 +63,19 @@ export function validateNoRefAccessInRender(fn: HIRFunction): void {
|
||||
}
|
||||
case "ObjectMethod":
|
||||
case "FunctionExpression": {
|
||||
/*
|
||||
* functions are allowed to capture refs, so long as the function is not called
|
||||
* during render. see AnalyzeFunctions for how we ensure that functions which
|
||||
* capture refs get assigned a mutable range so we know here whether the function
|
||||
* is called or not
|
||||
*/
|
||||
const mutableRange = instr.lvalue.identifier.mutableRange;
|
||||
if (mutableRange.end > mutableRange.start + 1) {
|
||||
for (const operand of eachInstructionValueOperand(instr.value)) {
|
||||
validateNonRefValue(error, operand);
|
||||
validateNonRefObject(error, operand);
|
||||
if (fn.env.config.validateRefAccessDuringRenderFunctionExpressions) {
|
||||
/*
|
||||
* functions are allowed to capture refs, so long as the function is not called
|
||||
* during render. see AnalyzeFunctions for how we ensure that functions which
|
||||
* capture refs get assigned a mutable range so we know here whether the function
|
||||
* is called or not
|
||||
*/
|
||||
const mutableRange = instr.lvalue.identifier.mutableRange;
|
||||
if (mutableRange.end > mutableRange.start + 1) {
|
||||
for (const operand of eachInstructionValueOperand(instr.value)) {
|
||||
validateNonRefValue(error, operand);
|
||||
validateNonRefObject(error, operand);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateRefAccessDuringRender
|
||||
// @validateRefAccessDuringRender @validateRefAccessDuringRenderFunctionExpressions
|
||||
function Component(props) {
|
||||
const ref = useRef(null);
|
||||
const renderItem = (item) => {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// @validateRefAccessDuringRender
|
||||
// @validateRefAccessDuringRender @validateRefAccessDuringRenderFunctionExpressions
|
||||
function Component(props) {
|
||||
const ref = useRef(null);
|
||||
const renderItem = (item) => {
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateRefAccessDuringRender:false
|
||||
function Foo({ a }) {
|
||||
const ref = useRef();
|
||||
const val = ref.current;
|
||||
@@ -15,7 +16,7 @@ function Foo({ a }) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
import { unstable_useMemoCache as useMemoCache } from "react"; // @validateRefAccessDuringRender:false
|
||||
function Foo(t20) {
|
||||
const $ = useMemoCache(4);
|
||||
const { a } = t20;
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// @validateRefAccessDuringRender:false
|
||||
function Foo({ a }) {
|
||||
const ref = useRef();
|
||||
const val = ref.current;
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateRefAccessDuringRender:false
|
||||
function Foo({ a }) {
|
||||
const ref = useRef();
|
||||
const x = { a, val: ref.current };
|
||||
@@ -14,7 +15,7 @@ function Foo({ a }) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
import { unstable_useMemoCache as useMemoCache } from "react"; // @validateRefAccessDuringRender:false
|
||||
function Foo(t17) {
|
||||
const $ = useMemoCache(4);
|
||||
const { a } = t17;
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// @validateRefAccessDuringRender:false
|
||||
function Foo({ a }) {
|
||||
const ref = useRef();
|
||||
const x = { a, val: ref.current };
|
||||
|
||||
Reference in New Issue
Block a user