From 939582dae02fc70c6b86f82492427249d0b3e1df Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Tue, 4 Apr 2023 12:30:11 -0700 Subject: [PATCH] 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`. --- .../useEffect-nested-lambdas.expect.md | 62 +++++++++++++++++++ .../compiler/useEffect-nested-lambdas.js | 21 +++++++ 2 files changed, 83 insertions(+) create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.js diff --git a/compiler/forget/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.expect.md new file mode 100644 index 0000000000..717093de0d --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.expect.md @@ -0,0 +1,62 @@ + +## Input + +```javascript +function Component(props) { + const item = useMutable(props.itemId); + const dispatch = useDispatch(); + + const exit = useCallback(() => { + dispatch(createExitAction()); + }, [dispatch]); + + useEffect(() => { + const cleanup = GlobalEventEmitter.addListener("onInput", () => { + if (item.value) { + exit(); + } + }); + return () => cleanup.remove(); + }, [exit, item]); + + maybeMutate(item); + + return
; +} + +``` + +## Code + +```javascript +function Component(props) { + const $ = React.unstable_useMemoCache(1); + const item = useMutable(props.itemId); + const dispatch = useDispatch(); + + const exit = () => { + dispatch(createExitAction()); + }; + + useEffect(() => { + const cleanup = GlobalEventEmitter.addListener("onInput", () => { + if (item.value) { + exit(); + } + }); + return () => cleanup.remove(); + }, [exit, item]); + + maybeMutate(item); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 =
; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.js b/compiler/forget/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.js new file mode 100644 index 0000000000..48da074a8f --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/useEffect-nested-lambdas.js @@ -0,0 +1,21 @@ +function Component(props) { + const item = useMutable(props.itemId); + const dispatch = useDispatch(); + + const exit = useCallback(() => { + dispatch(createExitAction()); + }, [dispatch]); + + useEffect(() => { + const cleanup = GlobalEventEmitter.addListener("onInput", () => { + if (item.value) { + exit(); + } + }); + return () => cleanup.remove(); + }, [exit, item]); + + maybeMutate(item); + + return
; +}