Skip to content

[eslint] Do not allow useEffectEvent fns to be called in arbitrary closures #33544

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

jbrown215
Copy link
Contributor

Summary:

useEffectEvent is meant to be used specifically in combination with useEffect, and using
the feature in arbitrary closures can lead to surprising reactivity semantics. In order to
minimize risk in the experimental rollout, we are going to restrict its usage to being
called directly inside an effect or another useEffectEvent, effectively enforcing the function
coloring statically. Without an effect system this is the best we can do.

…osures

Summary:

useEffectEvent is meant to be used specifically in combination with useEffect, and using
the feature in arbitrary closures can lead to surprising reactivity semantics. In order to
minimize risk in the experimental rollout, we are going to restrict its usage to being
called directly inside an effect or another useEffectEvent, effectively enforcing the function
coloring statically. Without an effect system this is the best we can do.
@react-sizebot
Copy link

Comparing: 5d24c64...0f26d04

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.68 kB 6.68 kB = 1.83 kB 1.83 kB
oss-stable/react-dom/cjs/react-dom-client.production.js +0.02% 530.47 kB 530.58 kB +0.02% 93.64 kB 93.66 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.69 kB 6.69 kB = 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js +0.02% 651.56 kB 651.67 kB +0.02% 114.75 kB 114.77 kB
facebook-www/ReactDOM-prod.classic.js +0.28% 674.72 kB 676.61 kB +0.25% 118.75 kB 119.05 kB
facebook-www/ReactDOM-prod.modern.js +0.25% 665.20 kB 666.89 kB +0.23% 117.17 kB 117.44 kB
facebook-www/ReactIs-prod.classic.js +12.53% 5.24 kB 5.89 kB +7.79% 1.27 kB 1.37 kB
facebook-www/ReactIs-prod.modern.js +12.53% 5.24 kB 5.89 kB +7.79% 1.27 kB 1.37 kB
facebook-www/ReactIs-dev.classic.js +12.04% 5.79 kB 6.49 kB +7.02% 1.31 kB 1.40 kB
facebook-www/ReactIs-dev.modern.js +12.04% 5.79 kB 6.49 kB +7.02% 1.31 kB 1.40 kB
facebook-www/React-dev.modern.js +3.91% 54.03 kB 56.14 kB +2.14% 12.05 kB 12.30 kB
facebook-www/React-dev.classic.js +3.91% 54.03 kB 56.15 kB +2.13% 12.05 kB 12.31 kB
facebook-www/JSXDEVRuntime-dev.classic.js +3.72% 12.89 kB 13.37 kB +2.36% 3.52 kB 3.60 kB
facebook-www/JSXDEVRuntime-dev.modern.js +3.72% 12.89 kB 13.37 kB +2.36% 3.52 kB 3.60 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
facebook-www/ReactIs-prod.classic.js +12.53% 5.24 kB 5.89 kB +7.79% 1.27 kB 1.37 kB
facebook-www/ReactIs-prod.modern.js +12.53% 5.24 kB 5.89 kB +7.79% 1.27 kB 1.37 kB
facebook-www/ReactIs-dev.classic.js +12.04% 5.79 kB 6.49 kB +7.02% 1.31 kB 1.40 kB
facebook-www/ReactIs-dev.modern.js +12.04% 5.79 kB 6.49 kB +7.02% 1.31 kB 1.40 kB
facebook-www/React-dev.modern.js +3.91% 54.03 kB 56.14 kB +2.14% 12.05 kB 12.30 kB
facebook-www/React-dev.classic.js +3.91% 54.03 kB 56.15 kB +2.13% 12.05 kB 12.31 kB
facebook-www/JSXDEVRuntime-dev.classic.js +3.72% 12.89 kB 13.37 kB +2.36% 3.52 kB 3.60 kB
facebook-www/JSXDEVRuntime-dev.modern.js +3.72% 12.89 kB 13.37 kB +2.36% 3.52 kB 3.60 kB
facebook-www/React-prod.modern.js +1.64% 20.67 kB 21.01 kB +1.02% 5.31 kB 5.36 kB
facebook-www/React-prod.classic.js +1.64% 20.68 kB 21.01 kB +1.04% 5.31 kB 5.36 kB
facebook-www/React-profiling.modern.js +1.61% 21.11 kB 21.45 kB +1.02% 5.39 kB 5.44 kB
facebook-www/React-profiling.classic.js +1.61% 21.11 kB 21.45 kB +1.02% 5.39 kB 5.44 kB
oss-stable-semver/react-is/cjs/react-is.development.js +0.72% 4.98 kB 5.02 kB +0.80% 1.12 kB 1.13 kB
oss-stable/react-is/cjs/react-is.development.js +0.72% 4.98 kB 5.02 kB +0.80% 1.12 kB 1.13 kB
oss-stable-semver/react-is/cjs/react-is.production.js +0.72% 4.44 kB 4.47 kB +1.12% 1.07 kB 1.08 kB
oss-stable/react-is/cjs/react-is.production.js +0.72% 4.44 kB 4.47 kB +1.12% 1.07 kB 1.08 kB
oss-experimental/react-is/cjs/react-is.development.js +0.72% 5.03 kB 5.06 kB +0.71% 1.13 kB 1.13 kB
oss-experimental/react-is/cjs/react-is.production.js +0.71% 4.49 kB 4.52 kB +1.21% 1.07 kB 1.09 kB
facebook-react-native/react-is/cjs/ReactIs-dev.js +0.70% 5.17 kB 5.21 kB +0.77% 1.17 kB 1.18 kB
facebook-react-native/react-is/cjs/ReactIs-prod.js +0.69% 4.65 kB 4.68 kB +1.23% 1.14 kB 1.15 kB
facebook-react-native/react-is/cjs/ReactIs-profiling.js +0.69% 4.65 kB 4.68 kB +1.23% 1.14 kB 1.15 kB
facebook-www/ReactART-prod.classic.js +0.49% 386.19 kB 388.09 kB +0.43% 64.72 kB 64.99 kB
facebook-www/ReactART-prod.modern.js +0.45% 376.41 kB 378.10 kB +0.39% 63.10 kB 63.34 kB
oss-stable-semver/react/cjs/react-jsx-dev-runtime.development.js +0.44% 11.79 kB 11.84 kB +0.46% 3.27 kB 3.29 kB
oss-stable/react/cjs/react-jsx-dev-runtime.development.js +0.44% 11.79 kB 11.84 kB +0.46% 3.27 kB 3.29 kB
oss-experimental/react/cjs/react-jsx-dev-runtime.development.js +0.44% 11.94 kB 11.99 kB +0.45% 3.31 kB 3.33 kB
facebook-react-native/react/cjs/JSXDEVRuntime-dev.js +0.43% 12.01 kB 12.07 kB +0.42% 3.31 kB 3.33 kB
oss-stable-semver/react/cjs/react-jsx-runtime.development.js +0.43% 12.19 kB 12.24 kB +0.43% 3.29 kB 3.30 kB
oss-stable/react/cjs/react-jsx-runtime.development.js +0.43% 12.19 kB 12.24 kB +0.43% 3.29 kB 3.30 kB
oss-experimental/react/cjs/react-jsx-runtime.development.js +0.42% 12.34 kB 12.39 kB +0.45% 3.33 kB 3.34 kB
facebook-react-native/react/cjs/JSXRuntime-dev.js +0.42% 12.42 kB 12.47 kB +0.42% 3.34 kB 3.35 kB
oss-stable-semver/react/cjs/react-jsx-runtime.react-server.development.js +0.40% 13.03 kB 13.09 kB +0.47% 3.42 kB 3.44 kB
oss-stable/react/cjs/react-jsx-runtime.react-server.development.js +0.40% 13.03 kB 13.09 kB +0.47% 3.42 kB 3.44 kB
oss-stable-semver/react/cjs/react-jsx-dev-runtime.react-server.development.js +0.40% 13.04 kB 13.09 kB +0.44% 3.42 kB 3.44 kB
oss-stable/react/cjs/react-jsx-dev-runtime.react-server.development.js +0.40% 13.04 kB 13.09 kB +0.44% 3.42 kB 3.44 kB
oss-experimental/react/cjs/react-jsx-runtime.react-server.development.js +0.39% 13.18 kB 13.23 kB +0.43% 3.46 kB 3.48 kB
oss-experimental/react/cjs/react-jsx-dev-runtime.react-server.development.js +0.39% 13.19 kB 13.24 kB +0.43% 3.46 kB 3.48 kB
facebook-www/ReactReconciler-prod.classic.js +0.39% 509.18 kB 511.18 kB +0.37% 81.22 kB 81.52 kB
facebook-www/ReactReconciler-prod.modern.js +0.36% 499.09 kB 500.87 kB +0.31% 79.68 kB 79.92 kB
facebook-www/ReactART-dev.modern.js +0.35% 721.45 kB 723.96 kB +0.32% 112.03 kB 112.39 kB
facebook-www/ReactART-dev.classic.js +0.34% 730.95 kB 733.46 kB +0.32% 113.78 kB 114.15 kB
facebook-www/ReactReconciler-dev.modern.js +0.30% 838.24 kB 840.78 kB +0.31% 129.81 kB 130.21 kB
facebook-www/ReactReconciler-dev.classic.js +0.30% 847.44 kB 849.98 kB +0.29% 131.59 kB 131.97 kB
facebook-www/ReactDOM-prod.classic.js +0.28% 674.72 kB 676.61 kB +0.25% 118.75 kB 119.05 kB
facebook-www/ReactDOMTesting-prod.classic.js +0.28% 689.12 kB 691.02 kB +0.24% 122.34 kB 122.63 kB
facebook-www/ReactDOM-profiling.modern.js +0.26% 736.93 kB 738.83 kB +0.24% 126.82 kB 127.12 kB
facebook-www/ReactDOM-profiling.classic.js +0.25% 744.98 kB 746.88 kB +0.24% 128.11 kB 128.41 kB
facebook-www/ReactDOM-prod.modern.js +0.25% 665.20 kB 666.89 kB +0.23% 117.17 kB 117.44 kB
facebook-www/ReactDOMTesting-prod.modern.js +0.25% 679.60 kB 681.30 kB +0.22% 120.81 kB 121.07 kB
facebook-www/ReactDOMServer-prod.classic.js +0.23% 253.25 kB 253.82 kB +0.28% 45.26 kB 45.38 kB
facebook-www/ReactDOM-dev.modern.js +0.22% 1,217.71 kB 1,220.37 kB +0.19% 201.03 kB 201.42 kB
facebook-www/ReactDOM-dev.classic.js +0.22% 1,226.85 kB 1,229.51 kB +0.20% 202.76 kB 203.17 kB
facebook-www/ReactDOMTesting-dev.modern.js +0.22% 1,234.25 kB 1,236.90 kB +0.19% 204.76 kB 205.15 kB
facebook-www/ReactDOMTesting-dev.classic.js +0.21% 1,243.39 kB 1,246.04 kB +0.19% 206.47 kB 206.86 kB
oss-experimental/react-dom/unstable_server-external-runtime.js = 18.29 kB 18.25 kB = 4.13 kB 4.11 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-server.node.development.js = 158.09 kB 157.38 kB = 28.92 kB 28.82 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-server.node.development.js = 158.09 kB 157.38 kB = 28.92 kB 28.82 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js = 153.42 kB 152.71 kB = 28.46 kB 28.36 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js = 153.42 kB 152.71 kB = 28.46 kB 28.36 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-server.edge.development.js = 152.63 kB 151.91 kB = 28.09 kB 28.02 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-server.edge.development.js = 152.63 kB 151.91 kB = 28.09 kB 28.02 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-server.browser.development.js = 149.06 kB 148.34 kB = 27.59 kB 27.51 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-server.browser.development.js = 149.06 kB 148.34 kB = 27.59 kB 27.51 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js = 166.13 kB 165.32 kB = 30.28 kB 30.18 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js = 166.13 kB 165.32 kB = 30.28 kB 30.18 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js = 166.08 kB 165.27 kB = 30.26 kB 30.16 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js = 166.08 kB 165.27 kB = 30.26 kB 30.16 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js = 164.93 kB 164.12 kB = 29.99 kB 29.89 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js = 164.93 kB 164.12 kB = 29.99 kB 29.89 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js = 160.50 kB 159.69 kB = 29.46 kB 29.37 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js = 160.50 kB 159.69 kB = 29.46 kB 29.37 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js = 160.47 kB 159.66 kB = 29.46 kB 29.37 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js = 160.47 kB 159.66 kB = 29.46 kB 29.37 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js = 157.44 kB 156.63 kB = 29.09 kB 29.00 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js = 157.44 kB 156.63 kB = 29.09 kB 29.00 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js = 156.89 kB 156.08 kB = 28.97 kB 28.87 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js = 156.89 kB 156.08 kB = 28.97 kB 28.87 kB
oss-stable-semver/react-server/cjs/react-server-flight.development.js = 110.12 kB 109.46 kB = 20.34 kB 20.27 kB
oss-stable/react-server/cjs/react-server-flight.development.js = 110.12 kB 109.46 kB = 20.34 kB 20.27 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-server.edge.development.js = 160.87 kB 159.69 kB = 29.52 kB 29.37 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-server.browser.development.js = 156.95 kB 155.77 kB = 29.00 kB 28.84 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js = 168.75 kB 167.46 kB = 30.92 kB 30.75 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js = 168.72 kB 167.44 kB = 30.93 kB 30.75 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js = 165.33 kB 164.05 kB = 30.48 kB 30.32 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js = 164.79 kB 163.51 kB = 30.36 kB 30.20 kB
oss-experimental/react-server/cjs/react-server-flight.development.js = 118.00 kB 116.87 kB = 21.76 kB 21.60 kB
facebook-www/ReactDOMServer-prod.modern.js = 250.89 kB 248.38 kB = 44.93 kB 44.65 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js = 185.26 kB 182.89 kB = 33.85 kB 33.50 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js = 185.21 kB 182.83 kB = 33.84 kB 33.49 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-server.node.development.js = 177.22 kB 174.94 kB = 32.48 kB 32.13 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js = 184.06 kB 181.68 kB = 33.55 kB 33.19 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js = 172.56 kB 170.28 kB = 32.01 kB 31.67 kB

Generated by 🚫 dangerJS against 0f26d04

@jbrown215
Copy link
Contributor Author

jbrown215 commented Jun 16, 2025

@rickhanlonii: I'm torn on the approach here and could use your perspective. We can't let the non-reactivity of useEffectEvent enter memoization dep arrays* (must be included in useCallback/useMemo) if we plan to let these functions be used in arbitrary closures. The two fixes for that problem are either:

  1. Force effect events to be included in memoization arrays but NOT dep arrays for useEffect
  2. Disallow effect effect events outside of other effect events/useEffect, effectively enforcing a strict function coloring.
    --
    The purpose of giving effect event unstable identities is to prevent them people from relying on it as a performance optimization / to ensure that it is easy to refactor parts of your useEffect into local useCallback functions:
const showToastEvent = useEffectEvent(showToast);
useEffect(() => {
  const connection = makeConnection();
  connection.on('message', msg => { showToastEvent(msg) });

  return () => conection.dispose();
}, []);

// Refactor
const showToastEvent = useEffectEvent(showToast);
const onMessage = useCallback(
  (connection) => connection.on('message',  msg => showToastEvent(msg));
), [?]);

useEffect(() => {
  const connection = makeConnection();
  onMessage(connection);
  return () => connection.dispose();
}, [onMessage]);

useEffectEvent fns are unstable, so if we include it in the [?] dep array then this refactor is a breaking change (re-runs on every render). If we exclude it in the [?] dep array then we aren't using the instability to invalidate memoization, breaking its purpose.

I put this version up because I think it is the better way forward, especially in the experimental release. Introducing differences in what you include in dep arrays depending on the API feels like a non-starter

if (
lastEffect == null &&
useEffectEventFunctions.has(node) &&
node.parent.type !== 'CallExpression'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The meaningful change here is that we error regardless of if the parent node is a call expression when we are not in an effect and the identifier is a uEE function. We tweak the error message based on whetehr it was called or not

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants