Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get ref from a React 16 portal?

Is there a way to get ref from a React 16 portal. I tried the following approach but it doesn't seem to work:

const Tooltip = props => (
  ReactDOM.createPortal(
    <div>{props.children}</div>,
    // A DOM element
    document.body
  )
);

class Info extends React.Component {
   render() {
      return (
        <Tooltip 
          ref={ el => this.tooltip = el }
        >
          My content
        </Tooltip>
      ); 
   }

   componentDidMount() {
      console.log(this.tooltip); // undefined
   }
}

I need the ref in order to dynamically calculate the element final position!

https://codepen.io/anon/pen/QqmBpB

like image 915
nanndoj Avatar asked Oct 09 '17 09:10

nanndoj


2 Answers

ReactDOM.createPortal returns a ReactPortal instance, which is a valid ReactNode but not a valid DOM element. At the same time createPortal will honour the component context. So I moved the function call to be inside the render method and it solved the issue.

class Info extends React.Component {
  render() {
    // I moved the portal creation to be here
    return ReactDOM.createPortal(
       // A valid DOM node!!
       <div ref={ el => this.tooltip = el }>{props.children}</div>,
       // A DOM element
       document.body
    ); 
  }

  componentDidMount() {
     console.log(this.tooltip); // HTMLDivElement
  }
}
like image 151
nanndoj Avatar answered Oct 01 '22 12:10

nanndoj


If you want to do it with hooks, you can also do

import { createPortal } from 'react-dom'
import React, { useRef } from 'react'

const Portal = ({ children }) => {
  const portal = useRef(document.createElement('div'))
  return createPortal(children, portal.current)
}

Also, in your exact example, you would need to use forwardRef since you can't pass refs to children without it.

const Tooltip = forwardRef((props, ref) => (
  createPortal(
    <div ref={ref}>{props.children}</div>,
    // A DOM element
    document.body
  )
));

const Info = () => {
  const ref = useRef()
  return (
    <Tooltip ref={ref}>
      My content
    </Tooltip>
  )
}

This last bit is not tested, but I'm pretty sure it'll work.

like image 29
Alex Cory Avatar answered Oct 01 '22 13:10

Alex Cory