Repro for unmemoized readonly callback
Repro of a closure that we currently treat as readonly because it captures a possibly-mutable value, but which we later realize is not mutable. Specifically, when we check `exit()` we think `dispatch()` is mutable and therefore consider it captured, which means we can't independently memoize `exit`.
Joe Savona committed
Apr 4, 2023 at 12:30 UTC
939582dae02fc70c6b86f82492427249d0b3e1df
2 files changed
+83
compiler/forget/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.expect.md
new
+62
@@ -0,0 +1,62 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ const item = useMutable(props.itemId);
7
+ const dispatch = useDispatch();
8
+
9
+ const exit = useCallback(() => {
10
+ dispatch(createExitAction());
11
+ }, [dispatch]);
12
+
13
+ useEffect(() => {
14
+ const cleanup = GlobalEventEmitter.addListener("onInput", () => {
15
+ if (item.value) {
16
+ exit();
17
+ }
18
+ });
19
+ return () => cleanup.remove();
20
+ }, [exit, item]);
21
+
22
+ maybeMutate(item);
23
+
24
+ return <div />;
25
+}
26
+
27
+```
28
+
29
+## Code
30
+
31
+```javascript
32
+function Component(props) {
33
+ const $ = React.unstable_useMemoCache(1);
34
+ const item = useMutable(props.itemId);
35
+ const dispatch = useDispatch();
36
+
37
+ const exit = () => {
38
+ dispatch(createExitAction());
39
+ };
40
+
41
+ useEffect(() => {
42
+ const cleanup = GlobalEventEmitter.addListener("onInput", () => {
43
+ if (item.value) {
44
+ exit();
45
+ }
46
+ });
47
+ return () => cleanup.remove();
48
+ }, [exit, item]);
49
+
50
+ maybeMutate(item);
51
+ let t0;
52
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
53
+ t0 = <div />;
54
+ $[0] = t0;
55
+ } else {
56
+ t0 = $[0];
57
+ }
58
+ return t0;
59
+}
60
+
61
+```
62
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.js
new
+21
@@ -0,0 +1,21 @@
1
+function Component(props) {
2
+ const item = useMutable(props.itemId);
3
+ const dispatch = useDispatch();
4
+
5
+ const exit = useCallback(() => {
6
+ dispatch(createExitAction());
7
+ }, [dispatch]);
8
+
9
+ useEffect(() => {
10
+ const cleanup = GlobalEventEmitter.addListener("onInput", () => {
11
+ if (item.value) {
12
+ exit();
13
+ }
14
+ });
15
+ return () => cleanup.remove();
16
+ }, [exit, item]);
17
+
18
+ maybeMutate(item);
19
+
20
+ return <div />;
21
+}