Browse Source

[beta] Add findDOMNode (#5357)

* [beta] Add findDOMNode

* info

Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
main
Ricky 2 years ago
committed by GitHub
parent
commit
c2566a56c0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 439
      beta/src/content/apis/react-dom/findDOMNode.md
  2. 3
      beta/src/sidebarAPIs.json

439
beta/src/content/apis/react-dom/findDOMNode.md

@ -2,27 +2,450 @@
title: findDOMNode
---
<Wip>
<Deprecated>
This section is incomplete, please see the old docs for [findDOMNode.](https://reactjs.org/docs/react-dom.html#finddomnode)
</Wip>
This API will be removed in a future major version of React.
</Deprecated>
<Intro>
If this component has been mounted into the DOM, this returns the corresponding native browser DOM element. This method is useful for reading values out of the DOM, such as form field values and performing DOM measurements. **In most cases, you can attach a ref to the DOM node and avoid using findDOMNode at all.**
`findDOMNode` finds the browser DOM node for a React [class component](/apis/react/Component) instance.
```js
findDOMNode(component)
```
const domNode = findDOMNode(componentInstance)
```
</Intro>
<InlineToc />
---
## Usage {/*usage*/}
### Finding the root DOM node of a class component {/*finding-the-root-dom-node-of-a-class-component*/}
<Deprecated>
This API will be removed in a future major version of React.
</Deprecated>
Call `findDOMNode` with a [class component](/apis/react/Component) instance (usually, `this`) to find the DOM node it has rendered.
```js {3}
class AutoselectingInput extends Component {
componentDidMount() {
const input = findDOMNode(this);
input.select()
}
render() {
return <input defaultValue="Hello" />
}
}
```
Here, the `input` variable will be set to the `<input>` DOM element. This lets you do something with it. For example, when clicking "Show example" below mounts the input, [`input.select()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/select) selects all text in the input:
<Sandpack>
```js App.js
import { useState } from 'react';
import AutoselectingInput from './AutoselectingInput.js';
export default function App() {
const [show, setShow] = useState(false);
return (
<>
<button onClick={() => setShow(true)}>
Show example
</button>
<hr />
{show && <AutoselectingInput />}
</>
);
}
```
```js AutoselectingInput.js active
import { Component } from 'react';
import { findDOMNode } from 'react-dom';
class AutoselectingInput extends Component {
componentDidMount() {
const input = findDOMNode(this);
input.select()
}
render() {
return <input defaultValue="Hello" />
}
}
export default AutoselectingInput;
```
</Sandpack>
<Pitfall>
`findDOMNode` is an escape hatch used to access the underlying DOM node. In most cases, use of this escape hatch is discouraged because it pierces the component abstraction. [It has been deprecated in StrictMode.](https://reactjs.org/docs/strict-mode.html#warning-about-deprecated-finddomnode-usage)
Using `findDOMNode` leads to fragile code. [See the alternatives.](#alternatives)
</Pitfall>
---
## Alternatives {/*alternatives*/}
### Reading component's own DOM node from a ref {/*reading-components-own-dom-node-from-a-ref*/}
Code using `findDOMNode` is fragile because the connection between the JSX node and the code manipulating the corresponding DOM node is not explicit. For example, try wrapping `<input />` from this example into a `<div>`:
<Sandpack>
```js App.js
import { useState } from 'react';
import AutoselectingInput from './AutoselectingInput.js';
export default function App() {
const [show, setShow] = useState(false);
return (
<>
<button onClick={() => setShow(true)}>
Show example
</button>
<hr />
{show && <AutoselectingInput />}
</>
);
}
```
```js AutoselectingInput.js active
import { Component } from 'react';
import { findDOMNode } from 'react-dom';
class AutoselectingInput extends Component {
componentDidMount() {
const input = findDOMNode(this);
input.select()
}
render() {
return <input defaultValue="Hello" />
}
}
export default AutoselectingInput;
```
</Sandpack>
This will break the code because now, `findDOMNode(this)` finds the `<div>` DOM node, but the code expects an `<input>` DOM node. To avoid these kinds of problems, use [`createRef`](/apis/react/createRef) to manage a specific DOM node.
In this example, `findDOMNode` is no longer used. Instead, `inputRef = createRef(null)` is defined as an instance field on the class. To read the DOM node from it, you can use `this.inputRef.current`. To attach it to the JSX, you render `<input ref={this.inputRef} />`. You have connected the code using the DOM node to its JSX:
<Sandpack>
```js App.js
import { useState } from 'react';
import AutoselectingInput from './AutoselectingInput.js';
export default function App() {
const [show, setShow] = useState(false);
return (
<>
<button onClick={() => setShow(true)}>
Show example
</button>
<hr />
{show && <AutoselectingInput />}
</>
);
}
```
```js AutoselectingInput.js active
import { createRef, Component } from 'react';
class AutoselectingInput extends Component {
inputRef = createRef(null);
componentDidMount() {
const input = this.inputRef.current;
input.select()
}
render() {
return (
<input ref={this.inputRef} defaultValue="Hello" />
);
}
}
export default AutoselectingInput;
```
</Sandpack>
In modern React without class components, the equivalent code would call [`useRef`](/apis/react/useRef) instead:
<Sandpack>
```js App.js
import { useState } from 'react';
import AutoselectingInput from './AutoselectingInput.js';
export default function App() {
const [show, setShow] = useState(false);
return (
<>
<button onClick={() => setShow(true)}>
Show example
</button>
<hr />
{show && <AutoselectingInput />}
</>
);
}
```
```js AutoselectingInput.js active
import { useRef, useEffect } from 'react';
export default function AutoselectingInput() {
const inputRef = useRef(null);
useEffect(() => {
const input = inputRef.current;
input.select();
}, []);
return <input ref={inputRef} defaultValue="Hello" />
}
```
</Sandpack>
[Read more about manipulating the DOM with refs.](/learn/manipulating-the-dom-with-refs)
---
### Reading a child component's DOM node from a forwarded ref {/*reading-a-child-components-dom-node-from-a-forwarded-ref*/}
In this example, `findDOMNode(this)` finds a DOM node that belongs to another component. The `AutoselectingInput` renders `MyInput`, which is your own component that renders a browser `<input>`.
<Sandpack>
```js App.js
import { useState } from 'react';
import AutoselectingInput from './AutoselectingInput.js';
export default function App() {
const [show, setShow] = useState(false);
return (
<>
<button onClick={() => setShow(true)}>
Show example
</button>
<hr />
{show && <AutoselectingInput />}
</>
);
}
```
```js AutoselectingInput.js active
import { Component } from 'react';
import { findDOMNode } from 'react-dom';
import MyInput from './MyInput.js';
class AutoselectingInput extends Component {
componentDidMount() {
const input = findDOMNode(this);
input.select()
}
render() {
return <MyInput />;
}
}
export default AutoselectingInput;
```
```js MyInput.js
export default function MyInput() {
return <input defaultValue="Hello" />;
}
```
</Sandpack>
Notice that calling `findDOMNode(this)` inside `AutoselectingInput` still gives you the DOM `<input>`--even though the JSX for this `<input>` is hidden inside the `MyInput` component. This seems convenient for the above example, but it leads to fragile code. Imagine that you wanted to edit `MyInput` later and add a wrapper `<div>` around it. This would break the code of `AutoselectingInput` (which expects to find an `<input>` DOM node).
To replace `findDOMNode` in this example, the two components need to coordinate:
1. `AutoSelectingInput` should declare a ref, like [in the earlier example](#reading-components-own-dom-node-from-a-ref), and pass it to `<MyInput>`.
2. `MyInput` should be declared with [`forwardRef`](/apis/react/forwardRef) to read the passed ref, and pass it down to the `<input>` node.
This version does that, so it no longer needs `findDOMNode`:
<Sandpack>
```js App.js
import { useState } from 'react';
import AutoselectingInput from './AutoselectingInput.js';
export default function App() {
const [show, setShow] = useState(false);
return (
<>
<button onClick={() => setShow(true)}>
Show example
</button>
<hr />
{show && <AutoselectingInput />}
</>
);
}
```
```js AutoselectingInput.js active
import { createRef, Component } from 'react';
import MyInput from './MyInput.js';
class AutoselectingInput extends Component {
inputRef = createRef(null);
componentDidMount() {
const input = this.inputRef.current;
input.select()
}
render() {
return (
<MyInput ref={this.inputRef} />
);
}
}
export default AutoselectingInput;
```
```js MyInput.js
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
return <input ref={ref} defaultValue="Hello" />;
});
export default MyInput;
```
</Sandpack>
Here is how this code would look like with function components instead of classes:
<Sandpack>
```js App.js
import { useState } from 'react';
import AutoselectingInput from './AutoselectingInput.js';
export default function App() {
const [show, setShow] = useState(false);
return (
<>
<button onClick={() => setShow(true)}>
Show example
</button>
<hr />
{show && <AutoselectingInput />}
</>
);
}
```
```js AutoselectingInput.js active
import { useRef, useEffect } from 'react';
import MyInput from './MyInput.js';
export default function AutoselectingInput() {
const inputRef = useRef(null);
useEffect(() => {
const input = inputRef.current;
input.select();
}, []);
return <MyInput ref={inputRef} defaultValue="Hello" />
}
```
```js MyInput.js
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
return <input ref={ref} defaultValue="Hello" />;
});
export default MyInput;
```
</Sandpack>
---
### Measuring position and size of the children {/*measuring-position-and-size-of-the-children*/}
Sometimes a component needs to know the position and size of its children. This makes it tempting to find the children with `findDOMNode(this)`, and then use DOM methods like [`getBoundingClientRect`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) for measurements.
There is currently no direct equivalent for this use case, which is why `findDOMNode` is deprecated but is not yet removed completely from React. In the meantime, you can try rendering a wrapper `<div>` node around the content as a workaround, and getting a ref to that node. However, extra wrappers can sometimes break styling.
```js
<div ref={someRef}>
{children}
</div>
```
This also applies to focusing and scrolling to arbitrary children.
---
## Reference {/*reference*/}
### `findDOMNode(componentInstance)` {/*finddomnode*/}
<Deprecated>
This API will be removed in a future major version of React.
</Deprecated>
Call `findDOMNode` to find the browser DOM node for a given React [class component](/apis/react/Component) instance.
```js
const domNode = findDOMNode(componentInstance);
```
[See examples above.](#usage)
#### Parameters {/*parameters*/}
* `componentInstance`: An instance of the [`Component`](/apis/react/Component) subclass. For example, `this` inside a class component.
#### Returns {/*returns*/}
`findDOMNode` returns the first closest browser DOM node within the given `componentInstance`. When a component renders to `null`, or renders `false`, `findDOMNode` returns `null`. When a component renders to a string, `findDOMNode` returns a text DOM node containing that value.
#### Caveats {/*caveats*/}
* A component may return an array or a [Fragment](/apis/react/Fragment) with multiple children. In that case `findDOMNode`, will return the DOM node corresponding to the first non-empty child.
* `findDOMNode` only works on mounted components (that is, components that have been placed in the DOM). If you try to call this on a component that has not been mounted yet (like calling `findDOMNode()` in `render()` on a component that has yet to be created), an exception will be thrown.
* `findDOMNode` only returns the result at the time of your call. If a child component renders a different node later, there is no way for you to be notified of this change.
* `findDOMNode` accepts a class component instance, so it can't be used with function components.

3
beta/src/sidebarAPIs.json

@ -132,8 +132,7 @@
},
{
"title": "findDOMNode",
"path": "/apis/react-dom/findDOMNode",
"wip": true
"path": "/apis/react-dom/findDOMNode"
},
{
"title": "hydrate",

Loading…
Cancel
Save