Skip to content

Commit bad34fc

Browse files
Add hanging ref troubleshooting section
1 parent 6ae99dd commit bad34fc

File tree

1 file changed

+84
-0
lines changed
  • src/content/reference/react-dom/components

1 file changed

+84
-0
lines changed

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

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,90 @@ To support backwards compatibility, if a cleanup function is not returned from t
282282
* 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.
283283
* When you pass a *different* `ref` callback, React will call the *previous* callback's cleanup function if provided. If no 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.
284284

285+
#### Troubleshooting: My ref points to a unmounted DOM node {/*my-ref-points-to-a-unmounted-dom-node*/}
286+
287+
A `ref` callback function with a cleanup function that does not set `ref.current` to `null` can result in a `ref` to a unmounted node. Uncheck "Show Input" below and click "Submit" to see how the `ref` to the unmounted `<input>` is still accessible by the click handler for the form.
288+
289+
<Sandpack>
290+
291+
```js
292+
import { useRef, useState } from "react";
293+
294+
export default function MyForm() {
295+
const [showInput, setShowInput] = useState(true);
296+
let inputRef = useRef();
297+
const handleCheckboxChange = (event) => {
298+
setShowInput(event.target.checked);
299+
};
300+
const handleSubmit = (event) => {
301+
event.preventDefault();
302+
if (inputRef.current) {
303+
alert(`Input value is: "${inputRef.current.value}"`);
304+
} else {
305+
alert("no input");
306+
}
307+
};
308+
const inputRefCallback = (node) => {
309+
inputRef.current = node;
310+
return () => {
311+
// ⚠️ You must set `ref.current` to `null`
312+
// in this cleanup function e.g.
313+
// `inputRef.current = null;`
314+
// to prevent hanging refs to unmounted DOM nodes
315+
};
316+
};
317+
318+
return (
319+
<form onSubmit={handleSubmit}>
320+
<div>
321+
<label>
322+
<input
323+
type="checkbox"
324+
checked={showInput}
325+
onChange={handleCheckboxChange}
326+
/>
327+
Show Input
328+
</label>
329+
</div>
330+
{showInput && (
331+
<div>
332+
<label>
333+
Input:
334+
<input
335+
type="text"
336+
defaultValue="value from input DOM node"
337+
ref={inputRefCallback}
338+
/>
339+
</label>
340+
</div>
341+
)}
342+
<button type="submit">Submit</button>
343+
</form>
344+
);
345+
}
346+
```
347+
348+
</Sandpack>
349+
350+
To fix the hanging ref to the DOM node that is no longer rendered set the `ref.current` to `null` in the ref callback cleanup function.
351+
352+
```js
353+
import { useRef } from "react";
354+
355+
function MyInput() {
356+
inputRef = useRef()
357+
const inputRefCallback = (node) => {
358+
ref.current = node;
359+
return () => {
360+
// ⚠️ You must set `ref.current` to `null` in this cleanup
361+
// function to prevent hanging refs to unmounted DOM nodes
362+
inputRef.current = null;
363+
};
364+
};
365+
return <input ref={inputRefCallback}>
366+
}
367+
```
368+
285369
---
286370

287371
### React event object {/*react-event-object*/}

0 commit comments

Comments
 (0)