From 49c2d26722fb1b5865ce0221a4cadc71b615e4cf Mon Sep 17 00:00:00 2001 From: Jack Pope Date: Sun, 28 Sep 2025 11:36:27 -0400 Subject: [PATCH 1/2] Update useEffectEvent docs for canary (#8025) * Update useEffectEvent docs for canary * Clean up prefixed imports * Fix import * Update blog post link --- ...labs-view-transitions-activity-and-more.md | 2 +- src/content/learn/escape-hatches.md | 6 +- .../learn/removing-effect-dependencies.md | 32 +++--- .../learn/reusing-logic-with-custom-hooks.md | 32 +++--- .../learn/separating-events-from-effects.md | 88 ++++++++------- .../react/experimental_useEffectEvent.md | 31 ------ src/content/reference/react/useEffect.md | 10 +- src/content/reference/react/useEffectEvent.md | 102 ++++++++++++++++++ src/sidebarReference.json | 5 + vercel.json | 5 + 10 files changed, 203 insertions(+), 110 deletions(-) delete mode 100644 src/content/reference/react/experimental_useEffectEvent.md create mode 100644 src/content/reference/react/useEffectEvent.md diff --git a/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md b/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md index c7f7400916..5d2c59b761 100644 --- a/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md +++ b/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md @@ -14303,7 +14303,7 @@ useEffect(() => { }); // compiler inserted dependencies. ``` -With this code, the React Compiler can infer the dependencies for you and insert them automatically so you don't need to see or write them. With features like [the IDE extension](#compiler-ide-extension) and [`useEffectEvent`](/reference/react/experimental_useEffectEvent), we can provide a CodeLens to show you what the Compiler inserted for times you need to debug, or to optimize by removing a dependency. This helps reinforce the correct mental model for writing Effects, which can run at any time to synchronize your component or hook's state with something else. +With this code, the React Compiler can infer the dependencies for you and insert them automatically so you don't need to see or write them. With features like [the IDE extension](#compiler-ide-extension) and [`useEffectEvent`](/reference/react/useEffectEvent), we can provide a CodeLens to show you what the Compiler inserted for times you need to debug, or to optimize by removing a dependency. This helps reinforce the correct mental model for writing Effects, which can run at any time to synchronize your component or hook's state with something else. Our hope is that automatically inserting dependencies is not only easier to write, but that it also makes them easier to understand by forcing you to think in terms of what the Effect does, and not in component lifecycles. diff --git a/src/content/learn/escape-hatches.md b/src/content/learn/escape-hatches.md index 0b2d595b2b..e6b050e0e9 100644 --- a/src/content/learn/escape-hatches.md +++ b/src/content/learn/escape-hatches.md @@ -455,8 +455,8 @@ This is not ideal. You want to re-connect to the chat only if the `roomId` has c ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -471,7 +471,7 @@ This is not ideal. You want to re-connect to the chat only if the `roomId` has c ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; diff --git a/src/content/learn/removing-effect-dependencies.md b/src/content/learn/removing-effect-dependencies.md index 7ab6dbc1f4..fb98a0cd14 100644 --- a/src/content/learn/removing-effect-dependencies.md +++ b/src/content/learn/removing-effect-dependencies.md @@ -609,11 +609,13 @@ function ChatRoom({ roomId }) { ### Do you want to read a value without "reacting" to its changes? {/*do-you-want-to-read-a-value-without-reacting-to-its-changes*/} - + -This section describes an **experimental API that has not yet been released** in a stable version of React. +**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** - +[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) + + Suppose that you want to play a sound when the user receives a new message unless `isMuted` is `true`: @@ -1262,8 +1264,8 @@ Is there a line of code inside the Effect that should not be reactive? How can y ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1277,7 +1279,7 @@ Is there a line of code inside the Effect that should not be reactive? How can y ```js import { useState, useEffect, useRef } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { FadeInAnimation } from './animation.js'; function Welcome({ duration }) { @@ -1389,8 +1391,8 @@ Your Effect needs to read the latest value of `duration`, but you don't want it ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1405,7 +1407,7 @@ Your Effect needs to read the latest value of `duration`, but you don't want it ```js import { useState, useEffect, useRef } from 'react'; import { FadeInAnimation } from './animation.js'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; function Welcome({ duration }) { const ref = useRef(null); @@ -1825,8 +1827,8 @@ Another of these functions only exists to pass some state to an imported API met ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1907,7 +1909,7 @@ export default function App() { ```js src/ChatRoom.js active import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export default function ChatRoom({ roomId, createConnection, onMessage }) { useEffect(() => { @@ -2120,8 +2122,8 @@ As a result, the chat re-connects only when something meaningful (`roomId` or `i ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -2189,7 +2191,7 @@ export default function App() { ```js src/ChatRoom.js active import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { createEncryptedConnection, createUnencryptedConnection, diff --git a/src/content/learn/reusing-logic-with-custom-hooks.md b/src/content/learn/reusing-logic-with-custom-hooks.md index de68dd190a..2038e59e16 100644 --- a/src/content/learn/reusing-logic-with-custom-hooks.md +++ b/src/content/learn/reusing-logic-with-custom-hooks.md @@ -837,11 +837,13 @@ Every time your `ChatRoom` component re-renders, it passes the latest `roomId` a ### Passing event handlers to custom Hooks {/*passing-event-handlers-to-custom-hooks*/} - + -This section describes an **experimental API that has not yet been released** in a stable version of React. +**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** - +[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) + + As you start using `useChatRoom` in more components, you might want to let components customize its behavior. For example, currently, the logic for what to do when a message arrives is hardcoded inside the Hook: @@ -985,7 +987,7 @@ export default function ChatRoom({ roomId }) { ```js src/useChatRoom.js import { useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { createConnection } from './chat.js'; export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { @@ -1070,8 +1072,8 @@ export function showNotification(message, theme = 'dark') { ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1666,7 +1668,7 @@ export default function App() { ```js src/useFadeIn.js active import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export function useFadeIn(ref, duration) { const [isRunning, setIsRunning] = useState(true); @@ -1719,8 +1721,8 @@ html, body { min-height: 300px; } ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -2208,8 +2210,8 @@ It looks like your `useInterval` Hook accepts an event listener as an argument. ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -2252,7 +2254,7 @@ export function useCounter(delay) { ```js src/useInterval.js import { useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export function useInterval(onTick, delay) { useEffect(() => { @@ -2279,8 +2281,8 @@ With this change, both intervals work as expected and don't interfere with each ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -2324,7 +2326,7 @@ export function useCounter(delay) { ```js src/useInterval.js active import { useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export function useInterval(callback, delay) { const onTick = useEffectEvent(callback); diff --git a/src/content/learn/separating-events-from-effects.md b/src/content/learn/separating-events-from-effects.md index fd603c3955..2a19e0f13a 100644 --- a/src/content/learn/separating-events-from-effects.md +++ b/src/content/learn/separating-events-from-effects.md @@ -400,13 +400,15 @@ You need a way to separate this non-reactive logic from the reactive Effect arou ### Declaring an Effect Event {/*declaring-an-effect-event*/} - + -This section describes an **experimental API that has not yet been released** in a stable version of React. +**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** - +[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) -Use a special Hook called [`useEffectEvent`](/reference/react/experimental_useEffectEvent) to extract this non-reactive logic out of your Effect: + + +Use a special Hook called [`useEffectEvent`](/reference/react/useEffectEvent) to extract this non-reactive logic out of your Effect: ```js {1,4-6} import { useEffect, useEffectEvent } from 'react'; @@ -448,8 +450,8 @@ Verify that the new behavior works as you would expect: ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -464,7 +466,7 @@ Verify that the new behavior works as you would expect: ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; @@ -578,11 +580,13 @@ You can think of Effect Events as being very similar to event handlers. The main ### Reading latest props and state with Effect Events {/*reading-latest-props-and-state-with-effect-events*/} - + + +**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** -This section describes an **experimental API that has not yet been released** in a stable version of React. +[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) - + Effect Events let you fix many patterns where you might be tempted to suppress the dependency linter. @@ -803,8 +807,8 @@ With `useEffectEvent`, there is no need to "lie" to the linter, and the code wor ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -818,7 +822,7 @@ With `useEffectEvent`, there is no need to "lie" to the linter, and the code wor ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export default function App() { const [position, setPosition] = useState({ x: 0, y: 0 }); @@ -878,11 +882,13 @@ Read [Removing Effect Dependencies](/learn/removing-effect-dependencies) for oth ### Limitations of Effect Events {/*limitations-of-effect-events*/} - + + +**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** -This section describes an **experimental API that has not yet been released** in a stable version of React. +[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) - + Effect Events are very limited in how you can use them: @@ -976,8 +982,8 @@ To fix this code, it's enough to follow the rules. ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1046,8 +1052,8 @@ If you remove the suppression comment, React will tell you that this Effect's co ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1124,8 +1130,8 @@ It seems like the Effect which sets up the timer "reacts" to the `increment` val ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1139,7 +1145,7 @@ It seems like the Effect which sets up the timer "reacts" to the `increment` val ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export default function Timer() { const [count, setCount] = useState(0); @@ -1193,8 +1199,8 @@ To solve the issue, extract an `onTick` Effect Event from the Effect: ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1208,7 +1214,7 @@ To solve the issue, extract an `onTick` Effect Event from the Effect: ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export default function Timer() { const [count, setCount] = useState(0); @@ -1275,8 +1281,8 @@ Code inside Effect Events is not reactive. Are there cases in which you would _w ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1290,7 +1296,7 @@ Code inside Effect Events is not reactive. Are there cases in which you would _w ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export default function Timer() { const [count, setCount] = useState(0); @@ -1362,8 +1368,8 @@ The problem with the above example is that it extracted an Effect Event called ` ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest" }, "scripts": { @@ -1377,7 +1383,7 @@ The problem with the above example is that it extracted an Effect Event called ` ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; export default function Timer() { const [count, setCount] = useState(0); @@ -1458,8 +1464,8 @@ Your Effect knows which room it connected to. Is there any information that you ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1474,7 +1480,7 @@ Your Effect knows which room it connected to. Is there any information that you ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; @@ -1599,8 +1605,8 @@ To fix the issue, instead of reading the *latest* `roomId` inside the Effect Eve ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1615,7 +1621,7 @@ To fix the issue, instead of reading the *latest* `roomId` inside the Effect Eve ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; @@ -1736,8 +1742,8 @@ To solve the additional challenge, save the notification timeout ID and clear it ```json package.json hidden { "dependencies": { - "react": "experimental", - "react-dom": "experimental", + "react": "canary", + "react-dom": "canary", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1752,7 +1758,7 @@ To solve the additional challenge, save the notification timeout ID and clear it ```js import { useState, useEffect } from 'react'; -import { experimental_useEffectEvent as useEffectEvent } from 'react'; +import { useEffectEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; diff --git a/src/content/reference/react/experimental_useEffectEvent.md b/src/content/reference/react/experimental_useEffectEvent.md deleted file mode 100644 index 954cf6587d..0000000000 --- a/src/content/reference/react/experimental_useEffectEvent.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: experimental_useEffectEvent -version: experimental ---- - - - -**This API is experimental and is not available in a stable version of React yet.** - -You can try it by upgrading React packages to the most recent experimental version: - -- `react@experimental` -- `react-dom@experimental` -- `eslint-plugin-react-hooks@experimental` - -Experimental versions of React may contain bugs. Don't use them in production. - - - - - - -`useEffectEvent` is a React Hook that lets you extract non-reactive logic into an [Effect Event.](/learn/separating-events-from-effects#declaring-an-effect-event) - -```js -const onSomething = useEffectEvent(callback) -``` - - - - diff --git a/src/content/reference/react/useEffect.md b/src/content/reference/react/useEffect.md index a250bb15a2..7ead26dffb 100644 --- a/src/content/reference/react/useEffect.md +++ b/src/content/reference/react/useEffect.md @@ -1691,11 +1691,13 @@ Now that you define the `createOptions` function inside the Effect, the Effect i ### Reading the latest props and state from an Effect {/*reading-the-latest-props-and-state-from-an-effect*/} - + -This section describes an **experimental API that has not yet been released** in a stable version of React. +**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** - +[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) + + By default, when you read a reactive value from an Effect, you have to add it as a dependency. This ensures that your Effect "reacts" to every change of that value. For most dependencies, that's the behavior you want. @@ -1710,7 +1712,7 @@ function Page({ url, shoppingCart }) { } ``` -**What if you want to log a new page visit after every `url` change, but *not* if only the `shoppingCart` changes?** You can't exclude `shoppingCart` from dependencies without breaking the [reactivity rules.](#specifying-reactive-dependencies) However, you can express that you *don't want* a piece of code to "react" to changes even though it is called from inside an Effect. [Declare an *Effect Event*](/learn/separating-events-from-effects#declaring-an-effect-event) with the [`useEffectEvent`](/reference/react/experimental_useEffectEvent) Hook, and move the code reading `shoppingCart` inside of it: + **What if you want to log a new page visit after every `url` change, but *not* if only the `shoppingCart` changes?** You can't exclude `shoppingCart` from dependencies without breaking the [reactivity rules.](#specifying-reactive-dependencies) However, you can express that you *don't want* a piece of code to "react" to changes even though it is called from inside an Effect. [Declare an *Effect Event*](/learn/separating-events-from-effects#declaring-an-effect-event) with the [`useEffectEvent`](/reference/react/useEffectEvent) Hook, and move the code reading `shoppingCart` inside of it: ```js {2-4,7,8} function Page({ url, shoppingCart }) { diff --git a/src/content/reference/react/useEffectEvent.md b/src/content/reference/react/useEffectEvent.md new file mode 100644 index 0000000000..8792f7ffb4 --- /dev/null +++ b/src/content/reference/react/useEffectEvent.md @@ -0,0 +1,102 @@ +--- +title: useEffectEvent +version: canary +--- + + + + +**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** + +[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) + + + + + +`useEffectEvent` is a React Hook that lets you extract non-reactive logic from your Effects into a reusable function called an [Effect Event](/learn/separating-events-from-effects#declaring-an-effect-event). + +```js +const onSomething = useEffectEvent(callback) +``` + + + + + +## Reference {/*reference*/} + +### `useEffectEvent(callback)` {/*useeffectevent*/} + +Call `useEffectEvent` at the top level of your component to declare an Effect Event. Effect Events are functions you can call inside Effects, such as `useEffect`: + +```js {4-6,11} +import { useEffectEvent, useEffect } from 'react'; + +function ChatRoom({ roomId, theme }) { + const onConnected = useEffectEvent(() => { + showNotification('Connected!', theme); + }); + + useEffect(() => { + const connection = createConnection(serverUrl, roomId); + connection.on('connected', () => { + onConnected(); + }); + connection.connect(); + return () => connection.disconnect(); + }, [roomId]); + + // ... +} +``` + +[See more examples below.](#usage) + +#### Parameters {/*parameters*/} + +- `callback`: A function containing the logic for your Effect Event. When you define an Effect Event with `useEffectEvent`, the `callback` always accesses the latest values from props and state when it is invoked. This helps avoid issues with stale closures. + +#### Returns {/*returns*/} + +Returns an Effect Event function. You can call this function inside `useEffect`, `useLayoutEffect`, or `useInsertionEffect`. + +#### Caveats {/*caveats*/} + +- **Only call inside Effects:** Effect Events should only be called within Effects. Define them just before the Effect that uses them. Do not pass them to other components or hooks. +- **Not a dependency shortcut:** Do not use `useEffectEvent` to avoid specifying dependencies in your Effect's dependency array. This can hide bugs and make your code harder to understand. Prefer explicit dependencies or use refs to compare previous values if needed. +- **Use for non-reactive logic:** Only use `useEffectEvent` to extract logic that does not depend on changing values. + +___ + +## Usage {/*usage*/} + +### Reading the latest props and state {/*reading-the-latest-props-and-state*/} + +Typically, when you access a reactive value inside an Effect, you must include it in the dependency array. This makes sure your Effect runs again whenever that value changes, which is usually the desired behavior. + +But in some cases, you may want to read the most recent props or state inside an Effect without causing the Effect to re-run when those values change. + +To [read the latest props or state](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events) in your Effect, without making those values reactive, include them in an Effect Event. + +```js {7-9,12} +import { useEffect, useContext, useEffectEvent } from 'react'; + +function Page({ url }) { + const { items } = useContext(ShoppingCartContext); + const numberOfItems = items.length; + + const onNavigate = useEffectEvent((visitedUrl) => { + logVisit(visitedUrl, numberOfItems); + }); + + useEffect(() => { + onNavigate(url); + }, [url]); + + // ... +} +``` + +You can pass reactive values like `url` as arguments to the Effect Event. This lets you access the latest values without making your Effect re-run for every change. + diff --git a/src/sidebarReference.json b/src/sidebarReference.json index 8823effb2e..67935b4597 100644 --- a/src/sidebarReference.json +++ b/src/sidebarReference.json @@ -38,6 +38,11 @@ "title": "useEffect", "path": "/reference/react/useEffect" }, + { + "title": "useEffectEvent", + "path": "/reference/react/useEffectEvent", + "version": "canary" + }, { "title": "useId", "path": "/reference/react/useId" diff --git a/vercel.json b/vercel.json index 1a55e20634..70ddefbe8b 100644 --- a/vercel.json +++ b/vercel.json @@ -248,6 +248,11 @@ "source": "/feed.xml", "destination": "/rss.xml", "permanent": true + }, + { + "source": "/reference/react/experimental_useEffectEvent", + "destination": "/reference/react/useEffectEvent", + "permanent": true } ], "headers": [ From b9064d0cd9b158c5443f53e81509fe942e93e866 Mon Sep 17 00:00:00 2001 From: Xleine Date: Tue, 30 Sep 2025 16:35:58 +0800 Subject: [PATCH 2/2] fix conflict --- ...labs-view-transitions-activity-and-more.md | 8 ++--- .../learn/removing-effect-dependencies.md | 8 ++--- .../learn/reusing-logic-with-custom-hooks.md | 8 ++--- .../learn/separating-events-from-effects.md | 30 +++++-------------- src/content/reference/react/Activity.md | 2 +- src/content/reference/react/useEffect.md | 14 ++------- src/content/reference/react/useEffectEvent.md | 4 +-- 7 files changed, 19 insertions(+), 55 deletions(-) diff --git a/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md b/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md index 864c344711..c4447188a1 100644 --- a/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md +++ b/src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md @@ -11469,7 +11469,7 @@ root.render( **`` 现在可以在 React Canary 版本使用。** -[了解更多关于 React 版本发布的内容。](/community/versioning-policy#all-release-channels) +[了解更多关于 React 版本发布的内容](/community/versioning-policy#all-release-channels)。 @@ -14303,11 +14303,7 @@ useEffect(() => { }); // 编译器插入的依赖项。 ``` -<<<<<<< HEAD -使用这段代码,React 编译器可以为你推断依赖项并自动插入它们,这样你就不需要看到或编写它们。通过像[IDE 扩展](#compiler-ide-extension)和[`useEffectEvent`](/reference/react/experimental_useEffectEvent)这样的功能,我们可以提供一个 CodeLens 来显示编译器在你需要调试时插入的内容,或通过移除依赖项来优化。这有助于强化编写 Effects 的正确心智模型,即 Effects 可以在任何时候运行,以将你的组件或 hook 的状态与其他内容同步。 -======= -With this code, the React Compiler can infer the dependencies for you and insert them automatically so you don't need to see or write them. With features like [the IDE extension](#compiler-ide-extension) and [`useEffectEvent`](/reference/react/useEffectEvent), we can provide a CodeLens to show you what the Compiler inserted for times you need to debug, or to optimize by removing a dependency. This helps reinforce the correct mental model for writing Effects, which can run at any time to synchronize your component or hook's state with something else. ->>>>>>> 49c2d26722fb1b5865ce0221a4cadc71b615e4cf +使用这段代码,React 编译器可以为你推断依赖项并自动插入它们,这样你就不需要看到或编写它们。通过像[IDE 扩展](#compiler-ide-extension)和[`useEffectEvent`](/reference/react/useEffectEvent)这样的功能,我们可以提供一个 CodeLens 来显示编译器在你需要调试时插入的内容,或通过移除依赖项来优化。这有助于强化编写 Effects 的正确心智模型,即 Effects 可以在任何时候运行,以将你的组件或 hook 的状态与其他内容同步。 我们希望自动插入依赖项不仅更容易编写,而且通过迫使你从 Effect 的作用角度思考,而不是从组件生命周期角度思考,使它们更容易理解。 diff --git a/src/content/learn/removing-effect-dependencies.md b/src/content/learn/removing-effect-dependencies.md index d6c2fd58b7..5ffb55aae5 100644 --- a/src/content/learn/removing-effect-dependencies.md +++ b/src/content/learn/removing-effect-dependencies.md @@ -612,13 +612,9 @@ function ChatRoom({ roomId }) { -<<<<<<< HEAD -本节描述了一个在稳定版本的 React 中 **尚未发布的实验性** API。 -======= -**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** ->>>>>>> 49c2d26722fb1b5865ce0221a4cadc71b615e4cf +**`useEffectEvent` API 当前仅在 React Canary 和 实验发行版中可用**。 -[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) +[了解更多关于 React 版本发布的内容](/community/versioning-policy#all-release-channels)。 diff --git a/src/content/learn/reusing-logic-with-custom-hooks.md b/src/content/learn/reusing-logic-with-custom-hooks.md index 0ad08abf7d..830b5b5fa0 100644 --- a/src/content/learn/reusing-logic-with-custom-hooks.md +++ b/src/content/learn/reusing-logic-with-custom-hooks.md @@ -839,13 +839,9 @@ export default function ChatRoom({ roomId }) { -<<<<<<< HEAD -这个章节描述了 React 稳定版 **还未发布的一个实验性 API**。 -======= -**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** ->>>>>>> 49c2d26722fb1b5865ce0221a4cadc71b615e4cf +**`useEffectEvent` API 当前仅在 React Canary 和 实验发行版中可用**。 -[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) +[了解更多关于 React 版本发布的内容](/community/versioning-policy#all-release-channels)。 diff --git a/src/content/learn/separating-events-from-effects.md b/src/content/learn/separating-events-from-effects.md index 971231ed62..6d2991e4c5 100644 --- a/src/content/learn/separating-events-from-effects.md +++ b/src/content/learn/separating-events-from-effects.md @@ -402,21 +402,13 @@ label { display: block; margin-top: 10px; } -<<<<<<< HEAD -本章节描述了一个在 React 稳定版中 **还没有发布的实验性 API**。 -======= -**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** ->>>>>>> 49c2d26722fb1b5865ce0221a4cadc71b615e4cf +**`useEffectEvent` API 当前仅在 React Canary 和 实验发行版中可用**。 -[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) +[了解更多关于 React 版本发布的内容](/community/versioning-policy#all-release-channels)。 -<<<<<<< HEAD -使用 [`useEffectEvent`](/reference/react/experimental_useEffectEvent) 这个特殊的 Hook 从 Effect 中提取非响应式逻辑: -======= -Use a special Hook called [`useEffectEvent`](/reference/react/useEffectEvent) to extract this non-reactive logic out of your Effect: ->>>>>>> 49c2d26722fb1b5865ce0221a4cadc71b615e4cf +使用 [`useEffectEvent`](/reference/react/useEffectEvent) 这个特殊的 Hook 从 Effect 中提取非响应式逻辑: ```js {1,4-6} import { useEffect, useEffectEvent } from 'react'; @@ -590,13 +582,9 @@ label { display: block; margin-top: 10px; } -<<<<<<< HEAD -本章节描述了一个在 React 稳定版中 **还没有发布的实验性 API**。 -======= -**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** ->>>>>>> 49c2d26722fb1b5865ce0221a4cadc71b615e4cf +**`useEffectEvent` API 当前仅在 React Canary 和 实验发行版中可用**。 -[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) +[了解更多关于 React 版本发布的内容](/community/versioning-policy#all-release-channels)。 @@ -896,13 +884,9 @@ body { -<<<<<<< HEAD -本章节描述了一个在 React 稳定版中 **还没有发布的实验性 API**。 -======= -**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** ->>>>>>> 49c2d26722fb1b5865ce0221a4cadc71b615e4cf +**`useEffectEvent` API 当前仅在 React Canary 和 实验发行版中可用**。 -[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) +[了解更多关于 React 版本发布的内容](/community/versioning-policy#all-release-channels)。 diff --git a/src/content/reference/react/Activity.md b/src/content/reference/react/Activity.md index c7d513afc3..ffd9ce5002 100644 --- a/src/content/reference/react/Activity.md +++ b/src/content/reference/react/Activity.md @@ -7,7 +7,7 @@ version: canary **The `` API is currently only available in React’s Canary and Experimental channels.** -[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) +[了解更多关于 React 版本发布的内容](/community/versioning-policy#all-release-channels)。 diff --git a/src/content/reference/react/useEffect.md b/src/content/reference/react/useEffect.md index 6b35ee1a96..df70801f95 100644 --- a/src/content/reference/react/useEffect.md +++ b/src/content/reference/react/useEffect.md @@ -1693,13 +1693,9 @@ button { margin-left: 10px; } -<<<<<<< HEAD -本节描述了一个 **实验性的 API**,它还没有在一个稳定的 React 版本中发布。 -======= -**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** ->>>>>>> 49c2d26722fb1b5865ce0221a4cadc71b615e4cf +**`useEffectEvent` API 当前仅在 React Canary 和 实验发行版中可用**。 -[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) +[了解更多关于 React 版本发布的内容](/community/versioning-policy#all-release-channels)。 @@ -1716,11 +1712,7 @@ function Page({ url, shoppingCart }) { } ``` -<<<<<<< HEAD -**如果你想在每次 `url` 更改后记录一次新的页面访问,而不是在 `shoppingCart` 更改后记录,该怎么办**?你不能在不违反 [响应规则](#specifying-reactive-dependencies) 的情况下将 `shoppingCart` 从依赖项中移除。然而,你可以表达你 **不希望** 某些代码对更改做出“响应”,即使它是在 Effect 内部调用的。使用 [`useEffectEvent`](/reference/react/experimental_useEffectEvent) Hook [声明 **Effect 事件**](/learn/separating-events-from-effects#declaring-an-effect-event),并将读取 `shoppingCart` 的代码移入其中: -======= - **What if you want to log a new page visit after every `url` change, but *not* if only the `shoppingCart` changes?** You can't exclude `shoppingCart` from dependencies without breaking the [reactivity rules.](#specifying-reactive-dependencies) However, you can express that you *don't want* a piece of code to "react" to changes even though it is called from inside an Effect. [Declare an *Effect Event*](/learn/separating-events-from-effects#declaring-an-effect-event) with the [`useEffectEvent`](/reference/react/useEffectEvent) Hook, and move the code reading `shoppingCart` inside of it: ->>>>>>> 49c2d26722fb1b5865ce0221a4cadc71b615e4cf +**如果你想在每次 `url` 更改后记录一次新的页面访问,而不是在 `shoppingCart` 更改后记录,该怎么办**?你不能在不违反 [响应规则](#specifying-reactive-dependencies) 的情况下将 `shoppingCart` 从依赖项中移除。然而,你可以表达你 **不希望** 某些代码对更改做出“响应”,即使它是在 Effect 内部调用的。使用 [`useEffectEvent`](/reference/react/useEffectEvent) Hook [声明 **Effect 事件**](/learn/separating-events-from-effects#declaring-an-effect-event),并将读取 `shoppingCart` 的代码移入其中: ```js {2-4,7,8} function Page({ url, shoppingCart }) { diff --git a/src/content/reference/react/useEffectEvent.md b/src/content/reference/react/useEffectEvent.md index 8792f7ffb4..5f16af0386 100644 --- a/src/content/reference/react/useEffectEvent.md +++ b/src/content/reference/react/useEffectEvent.md @@ -6,9 +6,9 @@ version: canary -**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.** +**`useEffectEvent` API 当前仅在 React Canary 和 实验发行版中可用**。 -[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels) +[了解更多关于 React 版本发布的内容](/community/versioning-policy#all-release-channels)。