Skip to content

Commit cdd2fdd

Browse files
authored
Add documentation for ref cleanup functions (#6770)
* Add documentation for ref cleanup functions * Contain changes within canary block
1 parent f8afd94 commit cdd2fdd

File tree

2 files changed

+79
-33
lines changed

2 files changed

+79
-33
lines changed

src/content/learn/manipulating-the-dom-with-refs.md

+56-31
Original file line numberDiff line numberDiff line change
@@ -218,18 +218,19 @@ This example shows how you can use this approach to scroll to an arbitrary node
218218
<Sandpack>
219219

220220
```js
221-
import { useRef } from 'react';
221+
import { useRef, useState } from "react";
222222

223223
export default function CatFriends() {
224224
const itemsRef = useRef(null);
225+
const [catList, setCatList] = useState(setupCatList);
225226

226-
function scrollToId(itemId) {
227+
function scrollToCat(cat) {
227228
const map = getMap();
228-
const node = map.get(itemId);
229+
const node = map.get(cat);
229230
node.scrollIntoView({
230-
behavior: 'smooth',
231-
block: 'nearest',
232-
inline: 'center'
231+
behavior: "smooth",
232+
block: "nearest",
233+
inline: "center",
233234
});
234235
}
235236

@@ -244,34 +245,25 @@ export default function CatFriends() {
244245
return (
245246
<>
246247
<nav>
247-
<button onClick={() => scrollToId(0)}>
248-
Tom
249-
</button>
250-
<button onClick={() => scrollToId(5)}>
251-
Maru
252-
</button>
253-
<button onClick={() => scrollToId(9)}>
254-
Jellylorum
255-
</button>
248+
<button onClick={() => scrollToCat(catList[0])}>Tom</button>
249+
<button onClick={() => scrollToCat(catList[5])}>Maru</button>
250+
<button onClick={() => scrollToCat(catList[9])}>Jellylorum</button>
256251
</nav>
257252
<div>
258253
<ul>
259-
{catList.map(cat => (
254+
{catList.map((cat) => (
260255
<li
261-
key={cat.id}
256+
key={cat}
262257
ref={(node) => {
263258
const map = getMap();
264259
if (node) {
265-
map.set(cat.id, node);
260+
map.set(cat, node);
266261
} else {
267-
map.delete(cat.id);
262+
map.delete(cat);
268263
}
269264
}}
270265
>
271-
<img
272-
src={cat.imageUrl}
273-
alt={'Cat #' + cat.id}
274-
/>
266+
<img src={cat} />
275267
</li>
276268
))}
277269
</ul>
@@ -280,12 +272,13 @@ export default function CatFriends() {
280272
);
281273
}
282274

283-
const catList = [];
284-
for (let i = 0; i < 10; i++) {
285-
catList.push({
286-
id: i,
287-
imageUrl: 'https://placekitten.com/250/200?image=' + i
288-
});
275+
function setupCatList() {
276+
const catList = [];
277+
for (let i = 0; i < 10; i++) {
278+
catList.push("https://loremflickr.com/320/240/cat?lock=" + i);
279+
}
280+
281+
return catList;
289282
}
290283

291284
```
@@ -316,6 +309,16 @@ li {
316309
}
317310
```
318311

312+
```json package.json hidden
313+
{
314+
"dependencies": {
315+
"react": "canary",
316+
"react-dom": "canary",
317+
"react-scripts": "^5.0.0"
318+
}
319+
}
320+
```
321+
319322
</Sandpack>
320323

321324
In this example, `itemsRef` doesn't hold a single DOM node. Instead, it holds a [Map](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map) from item ID to a DOM node. ([Refs can hold any values!](/learn/referencing-values-with-refs)) The [`ref` callback](/reference/react-dom/components/common#ref-callback) on every list item takes care to update the Map:
@@ -327,17 +330,39 @@ In this example, `itemsRef` doesn't hold a single DOM node. Instead, it holds a
327330
const map = getMap();
328331
if (node) {
329332
// Add to the Map
330-
map.set(cat.id, node);
333+
map.set(cat, node);
331334
} else {
332335
// Remove from the Map
333-
map.delete(cat.id);
336+
map.delete(cat);
334337
}
335338
}}
336339
>
337340
```
338341

339342
This lets you read individual DOM nodes from the Map later.
340343

344+
<Canary>
345+
346+
This example shows another approach for managing the Map with a `ref` callback cleanup function.
347+
348+
```js
349+
<li
350+
key={cat.id}
351+
ref={node => {
352+
const map = getMap();
353+
// Add to the Map
354+
map.set(cat, node);
355+
356+
return () => {
357+
// Remove from the Map
358+
map.delete(cat);
359+
};
360+
}}
361+
>
362+
```
363+
364+
</Canary>
365+
341366
</DeepDive>
342367

343368
## Accessing another component's DOM nodes {/*accessing-another-components-dom-nodes*/}

src/content/reference/react-dom/components/common.md

+23-2
Original file line numberDiff line numberDiff line change
@@ -257,11 +257,32 @@ React will also call your `ref` callback whenever you pass a *different* `ref` c
257257

258258
#### Parameters {/*ref-callback-parameters*/}
259259

260-
* `node`: A DOM node or `null`. React will pass you the DOM node when the ref gets attached, and `null` when the ref gets detached. Unless you pass the same function reference for the `ref` callback on every render, the callback will get temporarily detached and re-attached during every re-render of the component.
260+
* `node`: A DOM node or `null`. React will pass you the DOM node when the ref gets attached, and `null` when the `ref` gets detached. Unless you pass the same function reference for the `ref` callback on every render, the callback will get temporarily detached and re-attached during every re-render of the component.
261+
262+
<Canary>
261263

262264
#### Returns {/*returns*/}
263265

264-
Do not return anything from the `ref` callback.
266+
* **optional** `cleanup function`: When the `ref` is detached, React will call the cleanup function. If a function is not returned by the `ref` callback, React will call the callback again with `null` as the argument when the `ref` gets detached.
267+
268+
```js
269+
270+
<div ref={(node) => {
271+
console.log(node);
272+
273+
return () => {
274+
console.log('Clean up', node)
275+
}
276+
}}>
277+
278+
```
279+
280+
#### Caveats {/*caveats*/}
281+
282+
* When Strict Mode is on, React will **run one extra development-only setup+cleanup cycle** before the first real setup. This is a stress-test that ensures that your cleanup logic "mirrors" your setup logic and that it stops or undoes whatever the setup is doing. If this causes a problem, implement the cleanup function.
283+
* When you pass a *different* `ref` callback, React will call the *previous* callback's cleanup function if provided. If not cleanup function is defined, the `ref` callback will be called with `null` as the argument. The *next* function will be called with the DOM node.
284+
285+
</Canary>
265286

266287
---
267288

0 commit comments

Comments
 (0)