Skip to content

Commit a85ac54

Browse files
committed
Restore Activity form comparison
1 parent c04e245 commit a85ac54

1 file changed

Lines changed: 148 additions & 18 deletions

File tree

‎src/content/reference/react/Activity.md‎

Lines changed: 148 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -179,45 +179,175 @@ Changing `mode` preserves the state of the children. Removing the boundary or ch
179179

180180
An Activity boundary also preserves state held by the browser in DOM nodes.
181181

182-
For example, enter a draft in the contact form, hide it, and then show it again. The `<textarea>` value remains because its DOM node was hidden rather than removed.
182+
The Contact tab in the following examples contains an uncontrolled `<textarea>`. Enter a draft, switch to the Home tab, and then return to Contact to compare the behavior with and without Activity.
183+
184+
<Recipes titleText="The difference between removing and hiding a form" titleId="examples-preserving-dom-state">
185+
186+
#### Removing the form resets its state {/*removing-the-form-resets-its-state*/}
187+
188+
This version conditionally renders the active tab. Switching to Home removes `<Contact>` and its `<textarea>` from the DOM, so the draft is lost.
183189

184190
<Sandpack>
185191

186-
```js
192+
```js src/App.js active
193+
import { useState } from 'react';
194+
import Contact from './Contact.js';
195+
import Home from './Home.js';
196+
import TabButton from './TabButton.js';
197+
198+
export default function App() {
199+
const [activeTab, setActiveTab] = useState('contact');
200+
201+
return (
202+
<>
203+
<TabButton
204+
isActive={activeTab === 'home'}
205+
onClick={() => setActiveTab('home')}
206+
>
207+
Home
208+
</TabButton>
209+
<TabButton
210+
isActive={activeTab === 'contact'}
211+
onClick={() => setActiveTab('contact')}
212+
>
213+
Contact
214+
</TabButton>
215+
<hr />
216+
{activeTab === 'home' && <Home />}
217+
{activeTab === 'contact' && <Contact />}
218+
</>
219+
);
220+
}
221+
```
222+
223+
```js src/TabButton.js hidden
224+
export default function TabButton({ isActive, onClick, children }) {
225+
if (isActive) {
226+
return <b>{children}</b>;
227+
}
228+
229+
return <button onClick={onClick}>{children}</button>;
230+
}
231+
```
232+
233+
```js src/Home.js hidden
234+
export default function Home() {
235+
return <p>Welcome to my profile!</p>;
236+
}
237+
```
238+
239+
```js src/Contact.js hidden
240+
export default function Contact() {
241+
return (
242+
<div>
243+
<p>Send me a message!</p>
244+
<textarea aria-label="Message" />
245+
<p>You can find me online here:</p>
246+
<ul>
247+
<li>admin@mysite.com</li>
248+
<li>+123456789</li>
249+
</ul>
250+
</div>
251+
);
252+
}
253+
```
254+
255+
```css
256+
button,
257+
b {
258+
margin-right: 10px;
259+
}
260+
```
261+
262+
</Sandpack>
263+
264+
<Solution />
265+
266+
#### Hiding the form preserves its state {/*hiding-the-form-preserves-its-state*/}
267+
268+
This version renders both tabs in Activity boundaries. Switching tabs hides `<Contact>` without removing its DOM nodes, so the draft remains available when the tab becomes visible again.
269+
270+
<Sandpack>
271+
272+
```js src/App.js active
187273
import { Activity, useState } from 'react';
274+
import Contact from './Contact.js';
275+
import Home from './Home.js';
276+
import TabButton from './TabButton.js';
188277

189278
export default function App() {
190-
const [isShowingContact, setIsShowingContact] = useState(true);
279+
const [activeTab, setActiveTab] = useState('contact');
191280

192281
return (
193282
<>
194-
<button onClick={() => setIsShowingContact(showing => !showing)}>
195-
{isShowingContact ? 'Hide' : 'Show'} contact form
196-
</button>
197-
<Activity mode={isShowingContact ? 'visible' : 'hidden'}>
198-
<p>
199-
<label htmlFor="message">Message</label>
200-
<textarea id="message" />
201-
</p>
283+
<TabButton
284+
isActive={activeTab === 'home'}
285+
onClick={() => setActiveTab('home')}
286+
>
287+
Home
288+
</TabButton>
289+
<TabButton
290+
isActive={activeTab === 'contact'}
291+
onClick={() => setActiveTab('contact')}
292+
>
293+
Contact
294+
</TabButton>
295+
<hr />
296+
<Activity mode={activeTab === 'home' ? 'visible' : 'hidden'}>
297+
<Home />
298+
</Activity>
299+
<Activity mode={activeTab === 'contact' ? 'visible' : 'hidden'}>
300+
<Contact />
202301
</Activity>
203302
</>
204303
);
205304
}
206305
```
207306

208-
```css
209-
label,
210-
textarea {
211-
display: block;
307+
```js src/TabButton.js hidden
308+
export default function TabButton({ isActive, onClick, children }) {
309+
if (isActive) {
310+
return <b>{children}</b>;
311+
}
312+
313+
return <button onClick={onClick}>{children}</button>;
212314
}
213-
textarea {
214-
margin-top: 4px;
315+
```
316+
317+
```js src/Home.js hidden
318+
export default function Home() {
319+
return <p>Welcome to my profile!</p>;
320+
}
321+
```
322+
323+
```js src/Contact.js hidden
324+
export default function Contact() {
325+
return (
326+
<div>
327+
<p>Send me a message!</p>
328+
<textarea aria-label="Message" />
329+
<p>You can find me online here:</p>
330+
<ul>
331+
<li>admin@mysite.com</li>
332+
<li>+123456789</li>
333+
</ul>
334+
</div>
335+
);
336+
}
337+
```
338+
339+
```css
340+
button,
341+
b {
342+
margin-right: 10px;
215343
}
216344
```
217345

218346
</Sandpack>
219347

220-
The current value of an uncontrolled field belongs to its DOM node rather than React state. Activity preserves the node, so the value remains available when the boundary becomes visible again.
348+
<Solution />
349+
350+
</Recipes>
221351

222352
Activity also preserves other browser-managed state, such as scroll position and media playback position. In this example, play the video, hide it, and then show it again. When you play it again, the video continues from the same position.
223353

0 commit comments

Comments
 (0)