Skip to content

Commit 3c0296f

Browse files
committed
feedback and rm canary stuff
1 parent a7d0969 commit 3c0296f

File tree

9 files changed

+60
-110
lines changed

9 files changed

+60
-110
lines changed

src/content/blog/2025/04/23/react-labs-view-transitions-activity-and-more.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11542,7 +11542,7 @@ Try searching for a video, selecting it, and clicking "back":
1154211542
<Sandpack>
1154311543

1154411544
```js src/App.js
11545-
import { unstable_ViewTransition as ViewTransition, unstable_Activity as Activity } from "react"; import Details from "./Details"; import Home from "./Home"; import { useRouter } from "./router";
11545+
import { unstable_ViewTransition as ViewTransition, Activity } from "react"; import Details from "./Details"; import Home from "./Home"; import { useRouter } from "./router";
1154611546

1154711547
export default function App() {
1154811548
const { url } = useRouter();
@@ -12879,7 +12879,7 @@ With this update, if the content on the next page has time to pre-render, it wil
1287912879
<Sandpack>
1288012880

1288112881
```js src/App.js
12882-
import { unstable_ViewTransition as ViewTransition, unstable_Activity as Activity, use } from "react"; import Details from "./Details"; import Home from "./Home"; import { useRouter } from "./router"; import {fetchVideos} from './data'
12882+
import { unstable_ViewTransition as ViewTransition, Activity, use } from "react"; import Details from "./Details"; import Home from "./Home"; import { useRouter } from "./router"; import {fetchVideos} from './data'
1288312883

1288412884
export default function App() {
1288512885
const { url } = useRouter();

src/content/blog/2025/09/30/react-19-2.md

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,11 @@ React 19.2 is now available on npm!
1515

1616
</Intro>
1717

18+
This is our third release in the last year, following React 19 in December and React 19.1 in June. In this post, we'll give an overview of the new features in React 19.2, and highlight some notable changes.
1819

19-
<Note>
20-
21-
React Conf 2025 is October 7–8 in Henderson, Nevada!
20+
<InlineToc />
2221

23-
Watch the livestream on [the React Conf website](https://conf.react.dev).
24-
25-
</Note>
2622

27-
React 19.2 is our third release in the last year, following React 19 in December and React 19.1 in June.
28-
29-
This release continues to build on the scheduling capabilities of concurrent rendering with Activity, adds performance tracks to help optimize your code for concurrent rendering, and new APIs to improve the developer experience.
30-
31-
In this post, we'll give an overview of the new features in React 19.2, and how you can adopt them.
3223

3324
---
3425

@@ -38,19 +29,30 @@ In this post, we'll give an overview of the new features in React 19.2, and how
3829

3930
`<Activity>` lets you break your app into "activities" that can be controlled and prioritized.
4031

41-
In React 19.2, Activity supports two modes: `visible` and `hidden`:
32+
You can use Activity as an alternative to conditionally rendering parts of your app:
4233

4334
```js
35+
// Before
36+
{isVisible && <Page />}
37+
38+
// After
4439
<Activity mode={isVisible ? 'visible' : 'hidden'}>
4540
<Page />
4641
</Activity>
4742
```
4843

49-
When an Activity is `visible` it’s rendered as normal on the client, and will defer hydration during SSR. When an Activity is `hidden` it is unmounted (adding `display:none` to the children) and is excluded from the SSR output. While an Activity is hidden, React will keep the hook and DOM state, and continue to render at a lower priority than anything visible on screen.
50-
51-
You can use Activity to pre-render hidden parts of the app that a user is likely to navigate to next, or to save the state of parts the user navigates away from. This helps make navigations quicker, and allows back navigations to maintain state such as input fields.
44+
In React 19.2, Activity supports two modes: `visible` and `hidden`.
5245

53-
For more information, see the last [React Labs post](/blog/2025/04/23/react-labs-view-transitions-activity-and-more#activity) and the [Activity docs](/reference/react/activity).
46+
- `hidden`: hides the children, unmounts effects, and defers all updates until React has nothing left to work on.
47+
- `visible`: shows the children, mounts effects, and allows updates to be processed normally.
48+
49+
This means you can pre-render and keep rendering hidden parts of the app without impacting the performance of anything visible on screen.
50+
51+
You can use Activity to render hidden parts of the app that a user is likely to navigate to next, or to save the state of parts the user navigates away from. This helps make navigations quicker by loading data, css, and images in the background, and allows back navigations to maintain state such as input fields.
52+
53+
In the future, we plan to add more modes to Activity for different use cases.
54+
55+
For examples on how to use Activity, check out the [Activity docs](/reference/react/activity).
5456

5557
---
5658

@@ -76,29 +78,28 @@ For more information, see the [React Performance Tracks docs](/learn/react-perfo
7678

7779
### `useEffectEvent` {/*use-effect-event*/}
7880

79-
When using Effects 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.
81+
When using Effects you may want to read the most recent props or state inside an Effect without re-running the Effect when those values change. For example, in the following code the `theme` prop is used inside an Effect, but we don’t want the Effect to re-run when `theme` changes:
8082

81-
The typical workaround is to use a ref to store the latest `theme`, and read from that ref inside the Effect. This works, but it’s verbose and requires extra code, so in practice most users just disable the lint rule and leave the dependency out.
82-
83-
Disabling the lint rule creates a refactor hazard, because the linter cannot help you if you later add a dependency that should be included. Here, `threadId` is added after the initial implementation, but the Effect does not re-run when `threadId` changes because it was not added to the dependency array:
84-
85-
```js {1,3,12}
86-
function ChatRoom({ roomId, theme, threadId }) {
83+
```js {5,11}
84+
function ChatRoom({ roomId, theme }) {
8785
useEffect(() => {
88-
const connection = createConnection(serverUrl, roomId + threadId);
86+
const connection = createConnection(serverUrl, roomId);
8987
connection.on('connected', () => {
9088
showNotification('Connected!', theme);
9189
});
9290
connection.connect();
9391
return () => {
9492
connection.disconnect()
9593
};
96-
// eslint-disable-next-line react-hooks/exhaustive-deps
97-
}, [roomId]); // 🚩 threadId is missing!
94+
}, [roomId, theme]);
9895
// ...
9996
```
10097
101-
To solve this problem, React 19.2 introduces `useEffectEvent`, which lets you declare Effect Events that can be called inside Effects. Effect Events always access the latest values from props and state when they are invoked, so you can read the latest values without re-running the Effect.
98+
To solve this, most users just disable the lint rule and exclude the dependency. But that can lead to bugs since the linter can no longer help you keep the dependencies up to date if you need to update the Effect later.
99+
100+
React 19.2 introduces `useEffectEvent`, which lets you declare "Effect Events" that can be called inside Effects. Effect Events always access the latest values from props and state when they are invoked, so you can read the latest values without re-running the Effect:
101+
102+
With `useEffectEvent`, we can define the `onConnected` callback as an "Effect Event". Now, it doesn't re-run when the values change, but it can still “see” the latest values of your props and state:
102103
103104
```js {2,3,4,9}
104105
function ChatRoom({ roomId, theme, threadId }) {
@@ -117,6 +118,17 @@ function ChatRoom({ roomId, theme, threadId }) {
117118
// ...
118119
```
119120
121+
<Note>
122+
123+
#### Please don't over use `useEffectEvent` {/*please-dont-over-use-useeffectevent*/}
124+
125+
We introduced `useEffectEvent` as an experimental API several years ago, and you may be curious why it took so long to ship. We've considered multiple alternatives, but all of them have different tradeoffs that can make it too easy to accidentally out of reactivity, especially if it is overused.
126+
127+
We're shipping `useEffectEvent` because it helps solve a common problem, but we recommend using it sparingly. In the future we may explore other APIs that can solve the same problem better.
128+
129+
</Note>
130+
131+
120132
For more information, see [Separating Events from Effects](/learn/separating-events-from-effects) and the [`useEffectEvent` docs](/reference/react/useEffectEvent).
121133
122134
---
@@ -154,7 +166,7 @@ For more info, see the [`cacheSignal` docs](/reference/react/cacheSignal).
154166
155167
### Batching Suspense Boundaries for SSR {/*batching-suspense-boundaries-for-ssr*/}
156168
157-
We fixed a long-standing behavior bug where Suspense boundaries would reveal differently depending on if they were rendered on the client or when streaming from server-side rendering.
169+
We fixed a behavioral bug where Suspense boundaries would reveal differently depending on if they were rendered on the client or when streaming from server-side rendering.
158170
159171
Starting in 19.2, React will batch reveals of server-rendered Suspense boundaries for a short time, to allow more content to be revealed together and align with the client-rendered behavior.
160172
@@ -170,7 +182,7 @@ In React 19.2, suspense boundaries are batched for a small amount of time, to al
170182
171183
</Diagram>
172184
173-
This fix also prepares apps for supporting `<ViewTransition>` for Suspense during SSR. By revealing more content together, animations can run in larger batches of content, and avoid chaining animations of content that streams in close together.
185+
This fix also prepares apps for supporting `<ViewTransition>` for Suspense during SSR. By revealing more content together, animations can run in larger batches of content, and avoid chaining animations of content that stream in close together.
174186
175187
<Note>
176188
@@ -190,7 +202,14 @@ React 19.2 adds support for Web Streams for streaming SSR in Node.js:
190202
191203
<Pitfall>
192204
193-
We still highly recommend using Node Streams for streaming server-side rendering in Node environments because Node Streams are much faster than Web Streams, and Web Streams do not support compression by default, leading to users accidentally missing the benefits of streaming.
205+
#### Prefer Node Streams for server-side rendering in Node.js {/*prefer-node-streams-for-server-side-rendering-in-nodejs*/}
206+
207+
In Node.js environments, we still highly recommend using the Node Streams APIs:
208+
209+
- [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream)
210+
- [`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream)
211+
212+
This is because Node Streams are much faster than Web Streams in Node, and Web Streams do not support compression by default, leading to users accidentally missing the benefits of streaming.
194213
195214
</Pitfall>
196215
@@ -218,10 +237,10 @@ Notable bug fixes
218237
- `react`: Hide/unhide the content of dehydrated suspense boundaries if they resuspend [#32900](https://github.com/facebook/react/pull/32900)
219238
- `react`: Avoid stack overflow on wide trees during Hot Reload [#34145](https://github.com/facebook/react/pull/34145)
220239
- `react`: Improve component stacks in various places [#33629](https://github.com/facebook/react/pull/33629), [#33724](https://github.com/facebook/react/pull/33724), [#32735](https://github.com/facebook/react/pull/32735), [#33723](https://github.com/facebook/react/pull/33723)
221-
- `react`: Fix a bug with React.use inside React.lazy-ed Component (@hi-ogawa [#33941](https://github.com/facebook/react/pull/33941)
222-
- `react-dom`: Stop warning when ARIA 1.3 attributes are used (@Abdul-Omira [#34264](https://github.com/facebook/react/pull/34264)
240+
- `react`: Fix a bug with React.use inside React.lazy-ed Component [#33941](https://github.com/facebook/react/pull/33941)
241+
- `react-dom`: Stop warning when ARIA 1.3 attributes are used [#34264](https://github.com/facebook/react/pull/34264)
223242
- `react-dom`: Fix a bug with deeply nested Suspense inside Suspense fallbacks [#33467](https://github.com/facebook/react/pull/33467)
224-
- `react-dom`: Avoid hanging when suspending after aborting while rendering (@gnoff [#34192)]()
243+
- `react-dom`: Avoid hanging when suspending after aborting while rendering [#34192](https://github.com/facebook/react/pull/34192)
225244
226245
For a full list of changes, please see the [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md).
227246

src/content/learn/removing-effect-dependencies.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -609,14 +609,6 @@ function ChatRoom({ roomId }) {
609609
610610
### Do you want to read a value without "reacting" to its changes? {/*do-you-want-to-read-a-value-without-reacting-to-its-changes*/}
611611
612-
<Canary>
613-
614-
**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.**
615-
616-
[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels)
617-
618-
</Canary>
619-
620612
Suppose that you want to play a sound when the user receives a new message unless `isMuted` is `true`:
621613
622614
```js {3,10-12}

src/content/learn/reusing-logic-with-custom-hooks.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -837,14 +837,6 @@ Every time your `ChatRoom` component re-renders, it passes the latest `roomId` a
837837
838838
### Passing event handlers to custom Hooks {/*passing-event-handlers-to-custom-hooks*/}
839839
840-
<Canary>
841-
842-
**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.**
843-
844-
[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels)
845-
846-
</Canary>
847-
848840
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:
849841
850842
```js {9-11}

src/content/learn/separating-events-from-effects.md

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -400,14 +400,6 @@ You need a way to separate this non-reactive logic from the reactive Effect arou
400400
401401
### Declaring an Effect Event {/*declaring-an-effect-event*/}
402402
403-
<Canary>
404-
405-
**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.**
406-
407-
[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels)
408-
409-
</Canary>
410-
411403
Use a special Hook called [`useEffectEvent`](/reference/react/useEffectEvent) to extract this non-reactive logic out of your Effect:
412404
413405
```js {1,4-6}
@@ -580,14 +572,6 @@ You can think of Effect Events as being very similar to event handlers. The main
580572
581573
### Reading latest props and state with Effect Events {/*reading-latest-props-and-state-with-effect-events*/}
582574
583-
<Canary>
584-
585-
**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.**
586-
587-
[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels)
588-
589-
</Canary>
590-
591575
Effect Events let you fix many patterns where you might be tempted to suppress the dependency linter.
592576
593577
For example, say you have an Effect to log the page visits:
@@ -882,14 +866,6 @@ Read [Removing Effect Dependencies](/learn/removing-effect-dependencies) for oth
882866
883867
### Limitations of Effect Events {/*limitations-of-effect-events*/}
884868
885-
<Canary>
886-
887-
**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.**
888-
889-
[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels)
890-
891-
</Canary>
892-
893869
Effect Events are very limited in how you can use them:
894870
895871
* **Only call them from inside Effects.**

src/content/reference/react/Activity.md

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,7 @@
11
---
22
title: <Activity>
3-
version: canary
43
---
54

6-
<Canary>
7-
8-
**The `<Activity />` API is currently only available in React’s Canary and Experimental channels.**
9-
10-
[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels)
11-
12-
</Canary>
13-
145
<Intro>
156

167
`<Activity>` lets you hide and restore the UI and internal state of its children.
@@ -215,7 +206,7 @@ and check out the new behavior:
215206
<Sandpack>
216207

217208
```js src/App.js active
218-
import { unstable_Activity as Activity, useState } from 'react';
209+
import { Activity, useState } from 'react';
219210
import Sidebar from './Sidebar.js';
220211

221212
export default function App() {
@@ -434,7 +425,7 @@ If we switch to using an Activity boundary to show and hide the active tab, we c
434425
<Sandpack>
435426

436427
```js src/App.js active
437-
import { useState, unstable_Activity as Activity } from 'react';
428+
import { useState, Activity } from 'react';
438429
import TabButton from './TabButton.js';
439430
import Home from './Home.js';
440431
import Contact from './Contact.js';
@@ -712,7 +703,7 @@ Try clicking the Posts tab now:
712703
<Sandpack>
713704

714705
```js src/App.js
715-
import { useState, Suspense, unstable_Activity as Activity } from 'react';
706+
import { useState, Suspense, Activity } from 'react';
716707
import TabButton from './TabButton.js';
717708
import Home from './Home.js';
718709
import Posts from './Posts.js';
@@ -1136,7 +1127,7 @@ Let's update `App` to hide the inactive tab with a hidden Activity boundary inst
11361127
<Sandpack>
11371128

11381129
```js src/App.js active
1139-
import { useState, unstable_Activity as Activity } from 'react';
1130+
import { useState, Activity } from 'react';
11401131
import TabButton from './TabButton.js';
11411132
import Home from './Home.js';
11421133
import Video from './Video.js';
@@ -1270,7 +1261,7 @@ Let's see the new behavior. Try playing the video, switching to the Home tab, th
12701261
<Sandpack>
12711262

12721263
```js src/App.js active
1273-
import { useState, unstable_Activity as Activity } from 'react';
1264+
import { useState, Activity } from 'react';
12741265
import TabButton from './TabButton.js';
12751266
import Home from './Home.js';
12761267
import Video from './Video.js';

src/content/reference/react/useEffect.md

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1691,14 +1691,6 @@ Now that you define the `createOptions` function inside the Effect, the Effect i
16911691
16921692
### Reading the latest props and state from an Effect {/*reading-the-latest-props-and-state-from-an-effect*/}
16931693
1694-
<Canary>
1695-
1696-
**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.**
1697-
1698-
[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels)
1699-
1700-
</Canary>
1701-
17021694
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.
17031695
17041696
**However, sometimes you'll want to read the *latest* props and state from an Effect without "reacting" to them.** For example, imagine you want to log the number of the items in the shopping cart for every page visit:
@@ -1712,7 +1704,7 @@ function Page({ url, shoppingCart }) {
17121704
}
17131705
```
17141706
1715-
<CanaryBadge /> **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:
1707+
**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:
17161708
17171709
```js {2-4,7,8}
17181710
function Page({ url, shoppingCart }) {

src/content/reference/react/useEffectEvent.md

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,7 @@
11
---
22
title: useEffectEvent
3-
version: canary
43
---
54

6-
7-
<Canary>
8-
9-
**The `useEffectEvent` API is currently only available in React’s Canary and Experimental channels.**
10-
11-
[Learn more about React’s release channels here.](/community/versioning-policy#all-release-channels)
12-
13-
</Canary>
14-
155
<Intro>
166

177
`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).

src/sidebarReference.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
{
4242
"title": "useEffectEvent",
4343
"path": "/reference/react/useEffectEvent",
44-
"version": "canary"
4544
},
4645
{
4746
"title": "useId",
@@ -112,7 +111,6 @@
112111
{
113112
"title": "<Activity>",
114113
"path": "/reference/react/Activity",
115-
"version": "canary"
116114
},
117115
{
118116
"title": "<ViewTransition>",

0 commit comments

Comments
 (0)