mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[rhir] Patch: ordering of overlapping input dependencies does not matter
--- Patch and simplify logic around merging overlapping reactive dependencies. Added `reduce-reactive-unconditional-deps` test fixtures, which tries to cover all cases of merging unconditional dependencies (to a minimal dependencies set). Please let me know if I missed any
This commit is contained in:
@@ -189,24 +189,17 @@ class Context {
|
||||
(currentDeclaration.scope == null ||
|
||||
!this.#isScopeActive(currentDeclaration.scope))
|
||||
) {
|
||||
// Below logic ensures that `operand` is either added to `this.#dependencies`
|
||||
// directly, or is covered by an existing dependency.
|
||||
|
||||
// Check if there is an existing dependency that describes this operand
|
||||
for (const dep of this.#dependencies) {
|
||||
// not the same identifier
|
||||
if (dep.place.identifier.id !== maybeDependency.place.identifier.id) {
|
||||
continue;
|
||||
}
|
||||
const depPath = dep.path;
|
||||
// existing dep covers all paths
|
||||
if (depPath === null) {
|
||||
return;
|
||||
}
|
||||
const operandPath = maybeDependency.path;
|
||||
// existing dep is for a path, this operand covers all paths so swap them
|
||||
if (operandPath === null) {
|
||||
this.#dependencies.delete(dep);
|
||||
this.#dependencies.add(maybeDependency);
|
||||
return;
|
||||
}
|
||||
const depPath = dep.path ?? [];
|
||||
const operandPath = maybeDependency.path ?? [];
|
||||
// both the operand and dep have paths, determine if the existing path
|
||||
// is a subset of the new path
|
||||
let commonPathIndex = 0;
|
||||
@@ -218,7 +211,13 @@ class Context {
|
||||
commonPathIndex++;
|
||||
}
|
||||
if (commonPathIndex === depPath.length) {
|
||||
// existing dep is a subpath of the operand, so we don't need to
|
||||
// add the operand
|
||||
return;
|
||||
} else if (commonPathIndex === operandPath.length) {
|
||||
// operand is a subpath of the existing path, delete the existing
|
||||
// path
|
||||
this.#dependencies.delete(dep);
|
||||
}
|
||||
}
|
||||
this.#dependencies.add(maybeDependency);
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// Test that we can track non-overlapping dependencies separately.
|
||||
// (not needed for correctness but for dependency granularity)
|
||||
function TestNonOverlappingDescendantTracked(props) {
|
||||
let x = {};
|
||||
x.a = props.a.x.y;
|
||||
x.b = props.b;
|
||||
x.c = props.a.c.x.y.z;
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// Test that we can track non-overlapping dependencies separately.
|
||||
// (not needed for correctness but for dependency granularity)
|
||||
function TestNonOverlappingDescendantTracked(props) {
|
||||
const $ = React.unstable_useMemoCache(4);
|
||||
const c_0 = $[0] !== props.a.x.y;
|
||||
const c_1 = $[1] !== props.b;
|
||||
const c_2 = $[2] !== props.a.c.x.y.z;
|
||||
let x;
|
||||
if (c_0 || c_1 || c_2) {
|
||||
x = {};
|
||||
x.a = props.a.x.y;
|
||||
x.b = props.b;
|
||||
x.c = props.a.c.x.y.z;
|
||||
$[0] = props.a.x.y;
|
||||
$[1] = props.b;
|
||||
$[2] = props.a.c.x.y.z;
|
||||
$[3] = x;
|
||||
} else {
|
||||
x = $[3];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Test that we can track non-overlapping dependencies separately.
|
||||
// (not needed for correctness but for dependency granularity)
|
||||
function TestNonOverlappingDescendantTracked(props) {
|
||||
let x = {};
|
||||
x.a = props.a.x.y;
|
||||
x.b = props.b;
|
||||
x.c = props.a.c.x.y.z;
|
||||
return x;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// Test that we can track non-overlapping dependencies separately.
|
||||
// (not needed for correctness but for dependency granularity)
|
||||
function TestNonOverlappingTracked(props) {
|
||||
let x = {};
|
||||
x.b = props.a.b;
|
||||
x.c = props.a.c;
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// Test that we can track non-overlapping dependencies separately.
|
||||
// (not needed for correctness but for dependency granularity)
|
||||
function TestNonOverlappingTracked(props) {
|
||||
const $ = React.unstable_useMemoCache(3);
|
||||
const c_0 = $[0] !== props.a.b;
|
||||
const c_1 = $[1] !== props.a.c;
|
||||
let x;
|
||||
if (c_0 || c_1) {
|
||||
x = {};
|
||||
x.b = props.a.b;
|
||||
x.c = props.a.c;
|
||||
$[0] = props.a.b;
|
||||
$[1] = props.a.c;
|
||||
$[2] = x;
|
||||
} else {
|
||||
x = $[2];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// Test that we can track non-overlapping dependencies separately.
|
||||
// (not needed for correctness but for dependency granularity)
|
||||
function TestNonOverlappingTracked(props) {
|
||||
let x = {};
|
||||
x.b = props.a.b;
|
||||
x.c = props.a.c;
|
||||
return x;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// Test that we correctly track a subpath if the subpath itself is accessed as
|
||||
// a dependency
|
||||
function TestOverlappingDescendantTracked(props) {
|
||||
let x = {};
|
||||
x.b = props.a.b.c;
|
||||
x.c = props.a.b.c.x.y;
|
||||
x.a = props.a;
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// Test that we correctly track a subpath if the subpath itself is accessed as
|
||||
// a dependency
|
||||
function TestOverlappingDescendantTracked(props) {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
const c_0 = $[0] !== props.a;
|
||||
let x;
|
||||
if (c_0) {
|
||||
x = {};
|
||||
x.b = props.a.b.c;
|
||||
x.c = props.a.b.c.x.y;
|
||||
x.a = props.a;
|
||||
$[0] = props.a;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Test that we correctly track a subpath if the subpath itself is accessed as
|
||||
// a dependency
|
||||
function TestOverlappingDescendantTracked(props) {
|
||||
let x = {};
|
||||
x.b = props.a.b.c;
|
||||
x.c = props.a.b.c.x.y;
|
||||
x.a = props.a;
|
||||
return x;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// Test that we correctly track a subpath if the subpath itself is accessed as
|
||||
// a dependency
|
||||
function TestOverlappingTracked(props) {
|
||||
let x = {};
|
||||
x.b = props.a.b;
|
||||
x.c = props.a.c;
|
||||
x.a = props.a;
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// Test that we correctly track a subpath if the subpath itself is accessed as
|
||||
// a dependency
|
||||
function TestOverlappingTracked(props) {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
const c_0 = $[0] !== props.a;
|
||||
let x;
|
||||
if (c_0) {
|
||||
x = {};
|
||||
x.b = props.a.b;
|
||||
x.c = props.a.c;
|
||||
x.a = props.a;
|
||||
$[0] = props.a;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Test that we correctly track a subpath if the subpath itself is accessed as
|
||||
// a dependency
|
||||
function TestOverlappingTracked(props) {
|
||||
let x = {};
|
||||
x.b = props.a.b;
|
||||
x.c = props.a.c;
|
||||
x.a = props.a;
|
||||
return x;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// Determine that we only need to track p.a here
|
||||
// Ordering of access should not matter
|
||||
function TestDepsSubpathOrder1(props) {
|
||||
let x = {};
|
||||
x.b = props.a.b;
|
||||
x.a = props.a;
|
||||
x.c = props.a.b.c;
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// Determine that we only need to track p.a here
|
||||
// Ordering of access should not matter
|
||||
function TestDepsSubpathOrder1(props) {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
const c_0 = $[0] !== props.a;
|
||||
let x;
|
||||
if (c_0) {
|
||||
x = {};
|
||||
x.b = props.a.b;
|
||||
x.a = props.a;
|
||||
x.c = props.a.b.c;
|
||||
$[0] = props.a;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Determine that we only need to track p.a here
|
||||
// Ordering of access should not matter
|
||||
function TestDepsSubpathOrder1(props) {
|
||||
let x = {};
|
||||
x.b = props.a.b;
|
||||
x.a = props.a;
|
||||
x.c = props.a.b.c;
|
||||
return x;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// Determine that we only need to track p.a here
|
||||
// Ordering of access should not matter
|
||||
function TestDepsSubpathOrder2(props) {
|
||||
let x = {};
|
||||
x.a = props.a;
|
||||
x.b = props.a.b;
|
||||
x.c = props.a.b.c;
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// Determine that we only need to track p.a here
|
||||
// Ordering of access should not matter
|
||||
function TestDepsSubpathOrder2(props) {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
const c_0 = $[0] !== props.a;
|
||||
let x;
|
||||
if (c_0) {
|
||||
x = {};
|
||||
x.a = props.a;
|
||||
x.b = props.a.b;
|
||||
x.c = props.a.b.c;
|
||||
$[0] = props.a;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Determine that we only need to track p.a here
|
||||
// Ordering of access should not matter
|
||||
function TestDepsSubpathOrder2(props) {
|
||||
let x = {};
|
||||
x.a = props.a;
|
||||
x.b = props.a.b;
|
||||
x.c = props.a.b.c;
|
||||
return x;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// Determine that we only need to track p.a here
|
||||
// Ordering of access should not matter
|
||||
function TestDepsSubpathOrder3(props) {
|
||||
let x = {};
|
||||
x.c = props.a.b.c;
|
||||
x.a = props.a;
|
||||
x.b = props.a.b;
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// Determine that we only need to track p.a here
|
||||
// Ordering of access should not matter
|
||||
function TestDepsSubpathOrder3(props) {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
const c_0 = $[0] !== props.a;
|
||||
let x;
|
||||
if (c_0) {
|
||||
x = {};
|
||||
x.c = props.a.b.c;
|
||||
x.a = props.a;
|
||||
x.b = props.a.b;
|
||||
$[0] = props.a;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Determine that we only need to track p.a here
|
||||
// Ordering of access should not matter
|
||||
function TestDepsSubpathOrder3(props) {
|
||||
let x = {};
|
||||
x.c = props.a.b.c;
|
||||
x.a = props.a;
|
||||
x.b = props.a.b;
|
||||
return x;
|
||||
}
|
||||
Reference in New Issue
Block a user