Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ref can not replace ReactDOM.findDOMNode #9217

Closed
NE-SmallTown opened this issue Mar 19, 2017 · 10 comments
Closed

Ref can not replace ReactDOM.findDOMNode #9217

NE-SmallTown opened this issue Mar 19, 2017 · 10 comments

Comments

@NE-SmallTown
Copy link
Contributor

I notice that react will deprecated ReactDOM.findDOMNode and encourage developers to use ref callback instead.I agree with that in most cases it works fine.But think about something like this:

class A extends React.Component {
  render() {
    return (
      <div  id="containerA">
        <B><span>some content...</span></B>
     </div>
    );
  }
}

class B extends React.Component {
  render() {
    return (
      <div id="containerB">
         some cars....

         {this.props.children}
      </div>
    );
  }
}

For example,Now I want to know containerB's clientLeft,but I can't just add ref such as <B ref={ele => {this.containerB = ele;}}><span>some content...</span></B> because the ele is instance of B not the containerB dom node.

But ReactDOM.findDOMNode can do that,just use ReactDOM.findDOMNode(this.containerB ).clientLeft.

Of course we can add componentDidMout in the B and use <div id="containerB" ref={ele => {this.props.getContainerBClientLeft(ele.clientLeft)}}></div>,and in A,add a method called getContainerBClientLeft and pass it to B.But IMO, this make the app more complex.What's your thoughts?

@gaearon
Copy link
Collaborator

gaearon commented Mar 19, 2017

findDOMNode is not deprecated. If it was deprecated then you would see a deprecation notice when calling it and in the API reference.

It is discouraged in most cases (and people often use it unnecessarily), but it is useful exactly for the edge case you describe.

@gaearon gaearon closed this as completed Mar 19, 2017
@NE-SmallTown
Copy link
Contributor Author

NE-SmallTown commented Mar 20, 2017

Sorry,I am careless,the change log just say that it moved from react to react-dom.I am a little sensitive when I see the word "deprecated" 😢

@NE-SmallTown
Copy link
Contributor Author

@gaearon I notice that react-eslint has a rule and link to the previous discuss,you actually say that "We want to deprecate it eventually (not right now) ".

So you mean that you change your idea now?

@cPu1
Copy link

cPu1 commented Apr 4, 2017

You can also pass the ref callback to B via props, and attach the reference in A when the callback is invoked.

class A extends React.Component {
  render() {
    return (
      <div  id="containerA">
        <B bRef={ref => { this.containerB = ref; }}>
          <span>some content...</span>
        </B>
      </div>
    );
  }
}

class B extends React.Component {
  render() {
    return (
      <div id="containerB" ref={this.props.bRef}>
        some cars....
        {this.props.children}
      </div>
    );
  }
}

This is slightly better than defining a method on the component. The downside of this approach is having to pass the ref callback from the component needing the ref down to the component rendering a built-in component (native DOM element). It also requires passing ref to any components that will be refd, which isn't required with ReactDOM.findDOMNode.

@enisdenjo
Copy link

The thing is that when we try using some of the packages and their components which don't implement the flow @cPu1 described then we are left with two options. Recreate the actual component implementing the ref pass-down (which is NOT what you should do) or use the findDOMNode. This is the problem I experience a lot...

@gaearon
Copy link
Collaborator

gaearon commented Apr 6, 2017

Well you can use findDOMNode as an escape hatch. It is still available, just discouraged.

@mkaisercross
Copy link

I am glad I am not the only one worried about this feature being deprecated. I am currently using a 3rd party 'Webcam' component found here https://github.com/mozmorris/react-webcam. This component uses findDOMNode and I do not see how refs can be used to replace the functionality. Note: I do see that refs are being used here but if you look in the (very short) source code in src/react-webcam.js, it is also using findDOMNode.

Here is the simple example of how you can use this component.

class WebcamCapture extends React.Component {
  setRef = (webcam) => {
    this.webcam = webcam;
  }

  capture = () => {
    const imageSrc = this.webcam.getScreenshot();
  };

  render() {
    return (
      <div>
        <Webcam
          audio={false}
          height={350}
          ref={this.setRef}
          screenshotFormat="image/jpeg"
          width={350}
        />
        <button onClick={this.capture}>Capture photo</button>
      </div>
    );
  }
}

I think if you wanted to change this to use refs thenthe example code would require the user of this 3rd party library to understand the dom structure of the Webcam component which is using a video dom element and the getusermedia API. Requiring a 3rd party component user to understand the DOM structure of the component is not ideal to say the least. Please let me know if I am missing a simpler way to do this using refs.

@catamphetamine
Copy link
Contributor

Where can one find documentation about which methods other than the .focus() one are available on refs?
And in which React versions were they added.

@catamphetamine
Copy link
Contributor

catamphetamine commented Apr 25, 2018

Answering myself: turns out a ref is the DOM Element. So it has all of its methods:

<button ref={ref => this.button = ref}/>

But in case of React.Component elements the ref has no built-in DOM Element methods.
So findDOMNode() can be used there.

@kostia1st
Copy link

I'm looking for a findDOMNode-alike method for this use case:

  • you are NOT allowed to use class components, only function components with hooks (because of clean code and eslint rules etc);
  • you are creating a wrapper component, but you do NOT want to introduce any DOM elements (div or alike) within its rendering code, it just renders its children (because you normally don't want to deepen the DOM tree without any real need);
  • the component you're creating needs to get its hands on the immediate DOM element rendered by its children, to read out some stuff, or manipulate it safely;
  • you also want your wrapper component to be as universal as possible, so you don't want to depend on any specific API of the child components;

Is there any solution that meets all the points above?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants