diff --git a/src/content/reference/react/Activity.md b/src/content/reference/react/Activity.md index 8b103938e50..dd775631152 100644 --- a/src/content/reference/react/Activity.md +++ b/src/content/reference/react/Activity.md @@ -19,12 +19,11 @@ Experimental versions of React may contain bugs. Don't use them in production. -`` lets you hide and show part of the UI. - +`` lets you hide and restore the UI and internal state of its children. ```js - - + + ``` @@ -38,223 +37,270 @@ Experimental versions of React may contain bugs. Don't use them in production. ### `` {/*activity*/} -Wrap a part of the UI in `` to manage its visibility state: - -```js -import {unstable_Activity as Activity} from 'react'; +You can use Activity to hide part of your application: - - +```js [[1, 1, "\\"hidden\\""], [2, 2, ""], [3, 1, "\\"visible\\""]] + + ``` -When "hidden", the `children` of `` are not visible on the page. If a new `` mounts as "hidden" then it pre-renders the content at lower priority without blocking the visible content on the page, but it does not mount by creating Effects. When a "visible" Activity switches to "hidden" it conceptually unmounts by destroying all the Effects, but saves its state. This allows fast switching between "visible" and "hidden" states without recreating the state for a "hidden" Activity. +When an Activity boundary becomes hidden, React will visually hide its children using the `display: "none"` CSS property. It will also destroy their Effects, cleaning up any active subscriptions. + +While hidden, children still re-render in response to new props, albeit at a lower priority than the rest of the content. -In the future, "hidden" Activities may automatically destroy state based on resources like memory. +When the boundary becomes visible again, React will reveal the children with their previous state restored, and create their Effects. + +In this way, Activity can thought of as a mechanism for rendering "background activity". Rather than unmounting content that's likely to become visible again, you can use Activity to maintain and restore that content's UI and internal state. + +[See more examples below.](#usage) #### Props {/*props*/} -* `children`: The actual UI you intend to render. -* **optional** `mode`: Either "visible" or "hidden". Defaults to "visible". When "hidden", updates to the children are deferred to lower priority. The component will not create Effects until the Activity is switched to "visible". If a "visible" Activity switches to "hidden", the Effects will be destroyed. +* `children`: The UI you intend to show and hide. +* `mode`: A string value of either `'visible'` or `'hidden'`. If omitted, defaults to `'visible'`. #### Caveats {/*caveats*/} -- While hidden, the `children` of `` are hidden on the page. -- `` will unmount all Effects when switching from "visible" to "hidden" without destroying React or DOM state. This means Effects that are expected to run only once on mount will run again when switching from "hidden" to "visible". Conceptually, "hidden" Activities are unmounted, but they are not destroyed either. We recommend using [``](/reference/react/StrictMode) to catch any unexpected side-effects from this behavior. - When used with ``, hidden activities that reveal in a transition will activate an "enter" animation. Visible Activities hidden in a transition will activate an "exit" animation. -- Parts of the UI wrapped in `` are not included in the SSR response. -- Parts of the UI wrapped in `` will hydrate at a lower priority than other content. --- ## Usage {/*usage*/} -### Pre-render part of the UI {/*pre-render-part-of-the-ui*/} +### Restoring the state of hidden components {/*restoring-the-state-of-hidden-components*/} -You can pre-render part of the UI using ``: +Typically in React, when you want to conditionally show or hide a component, you mount and unmount it: -```js - - +```jsx +{isShowingSidebar && ( + +)} +``` + +But unmounting a component destroys its internal state, which is not always what you want. + +When you hide a component using an Activity boundary instead, React will "save" its state for later: + +```jsx + + ``` -When an Activity is rendered with `mode="hidden"`, the `children` are not visible on the page, but are rendered at lower priority than the visible content on the page. +This makes it possible to restore components to their previous state. -When the `mode` later switches to "visible", the pre-rendered children will mount and become visible. This can be used to prepare parts of the UI the user is likely to interact with next to reduce loading times. +The following example has a sidebar with an expandable section – you can press "Overview" to reveal the three subitems below it. The main app area also has a button that hides and shows the sidebar. -In the following example from [`useTransition`](/reference/react/useTransition#preventing-unwanted-loading-indicators), the `PostsTab` component fetches some data using `use`. When you click the β€œPosts” tab, the `PostsTab` component suspends, causing the button loading state to appear: +Try expanding the Overview section, then toggling the sidebar closed then open: -```js -import { Suspense, useState } from 'react'; -import TabButton from './TabButton.js'; -import AboutTab from './AboutTab.js'; -import PostsTab from './PostsTab.js'; -import ContactTab from './ContactTab.js'; +```js src/App.js active +import { useState } from 'react'; +import Sidebar from './Sidebar.js'; + +export default function App() { + const [isShowingSidebar, setIsShowingSidebar] = useState(true); -export default function TabContainer() { - const [tab, setTab] = useState('about'); return ( - πŸŒ€ Loading...}> - setTab('about')} - > - About - - setTab('posts')} - > - Posts - - setTab('contact')} - > - Contact - -
- {tab === 'about' && } - {tab === 'posts' && } - {tab === 'contact' && } -
+ <> + {isShowingSidebar && ( + + )} + +
+ +

Main content

+
+ ); } ``` +```js src/Sidebar.js +import { useState } from 'react'; -```js src/TabButton.js active -import { useTransition } from 'react'; - -export default function TabButton({ action, children, isActive }) { - const [isPending, startTransition] = useTransition(); - if (isActive) { - return {children} - } - if (isPending) { - return {children}; - } +export default function Sidebar() { + const [isExpanded, setIsExpanded] = useState(false) + return ( - + ); } ``` -```js src/AboutTab.js hidden -import {unstable_ViewTransition as ViewTransition} from 'react'; +```css +body { height: 275px; margin: 0; } +#root { + display: flex; + gap: 10px; + height: 100%; +} +nav { + padding: 10px; + background: #eee; + font-size: 14px; + height: 100%; +} +main { + padding: 10px; +} +p { + margin: 0; +} +h1 { + margin-top: 10px; +} +.indicator { + margin-left: 4px; + display: inline-block; + rotate: 90deg; +} +.indicator.down { + rotate: 180deg; +} +``` -export default function AboutTab() { - return ( - -

Welcome to my profile!

-
- ); +```json package.json hidden +{ + "dependencies": { + "react": "experimental", + "react-dom": "experimental", + "react-scripts": "latest", + "toastify-js": "1.12.0" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } } ``` -```js src/PostsTab.js hidden -import {use, unstable_ViewTransition as ViewTransition} from 'react'; -import { fetchData } from './data.js'; +
-function PostsTab() { - const posts = use(fetchData('/posts')); - return ( - -
    - {posts.map(post => - - )} -
-
- ); -} +The Overview section always starts out collapsed. Because we unmount the sidebar when `isShowingSidebar` flips to `false`, all its internal state is lost. -function Post({ title }) { - return ( -
  • - {title} -
  • - ); -} +This is a perfect use case for Activity. We can preserve the internal state of our sidebar, even when visually hiding it. -export default PostsTab; -``` +Let's replace the conditional rendering of our sidebar with an Activity boundary: -```js src/ContactTab.js hidden -import {unstable_ViewTransition as ViewTransition} from 'react'; +```jsx {7,9} +// Before +{isShowingSidebar && ( + +)} -export default function ContactTab() { - return ( - -

    - Send me a message! -

    -