@samitouri / QOS-React-1 / commits / 3a6a2e7e4a

Repro for "context variables are always mutable" error w callbacks

I haven't debugged to understand exactly why this pattern fails, but there are a few instances of this internally. It's especially weird because ```javascript // @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions function Component(props) { const [_state, setState] = useState(); const a = () => { return b(); }; const b = () => { return ( <> <div onClick={() => onClick(true)} /> <div onClick={() => onClick(false)} /> // <---- only repros if there's a second call! </> ); }; const onClick = (value) => { setState(value); }; return <div>{a()}</div>; } ``` Here, if `b()` only had one nested function expression that called `onClick` it would work. Also, if we disable `@enableTransitivelyFreezeFunctionExpressions` then it works. But the combination of multiple calls plus that mode causes "context variables are always mutable". I'm guessing we're freezing `onClick` twice and the second time reports an error since it calls `setState`.

Joe Savona committed Mar 21, 2024 at 21:40 UTC 3a6a2e7e4a807003bc42e103d157ae783602c362
2 files changed +71
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-multiple-calls-to-hoisted-callback-from-other-callback.expect.md new
+46
@@ -0,0 +1,46 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions
6 +function Component(props) {
7 + const [_state, setState] = useState();
8 + const a = () => {
9 + return b();
10 + };
11 + const b = () => {
12 + return (
13 + <>
14 + <div onClick={() => onClick(true)} />
15 + <div onClick={() => onClick(false)} />
16 + </>
17 + );
18 + };
19 + const onClick = (value) => {
20 + setState(value);
21 + };
22 +
23 + return <div>{a()}</div>;
24 +}
25 +
26 +export const FIXTURE_ENTRYPONT = {
27 + fn: Component,
28 + props: [{}],
29 +};
30 +
31 +```
32 +
33 +
34 +## Error
35 +
36 +```
37 + 9 | <>
38 + 10 | <div onClick={() => onClick(true)} />
39 +> 11 | <div onClick={() => onClick(false)} />
40 + | ^^^^^^^ [ReactForget] Invariant: [InferReferenceEffects] Context variables are always mutable. (11:11)
41 + 12 | </>
42 + 13 | );
43 + 14 | };
44 +```
45 +
46 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-multiple-calls-to-hoisted-callback-from-other-callback.js new
+25
@@ -0,0 +1,25 @@
1 +// @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions
2 +function Component(props) {
3 + const [_state, setState] = useState();
4 + const a = () => {
5 + return b();
6 + };
7 + const b = () => {
8 + return (
9 + <>
10 + <div onClick={() => onClick(true)} />
11 + <div onClick={() => onClick(false)} />
12 + </>
13 + );
14 + };
15 + const onClick = (value) => {
16 + setState(value);
17 + };
18 +
19 + return <div>{a()}</div>;
20 +}
21 +
22 +export const FIXTURE_ENTRYPONT = {
23 + fn: Component,
24 + props: [{}],
25 +};