0

I'm using a component library, which renders an input field nested within several div elements

<ComponentLibrary />

This component library renders a tree similar to the one below:

<div className={outerDiv}>
  <div>
    <input type="text" />  // This the element I'm trying to access
  </div>
</div>

I built a custom component which wraps the ComponentLibrary similar to below:

<MyCustomComponent>
  <ComponentLibrary />
</MyCustomComponent>

I don't have access to modify the component library, but I need to access it's input field within my component.

const MyCustomComponent = (props) => {
  const inputRef = useRef();

  return (
    {React.cloneElement(props.children, {
      ref: inputRef // This ofcourse references the 'outerDiv'
    }}
  );
}

Some possible relevant notes, when I console.log(props.children). While the component is react element, the type is a forward ref.

> children:
  $$typeof: Symbol(react.element)
  ref: null
  > type: {$$typeof: Symbol(react.forward_ref)}

Below are a few more attempts I made:

{React.Children.map(props.children, child => {
   console.log(child) // Only logs the Component element. No reference of DOM nodes
}

The closest I've gotten is by using the inputRef I created in the 4th code block above.

console.log(inputRef.current)
// Logs `outerDiv` element

console.log(inputRef.current.children[0]?.children[1]?.children[1])
// Returns the right element, but an absolute mess. Is this my best solution?
bruh
  • 2,205
  • 7
  • 30
  • 42
  • What is the component library you're using? – 5eb Apr 16 '21 at 20:05
  • @BasvanderLinden I'm using an internal component library built by a team in my company, who works independently from mine. – bruh Apr 18 '21 at 20:41
  • What is the documented interface of `ComponentLibrary`? It would be preferable to use that than to use React internals in such a way that is likely to break the next time `ComponentLibrary`'s implementation updates. – Patrick Roberts Apr 18 '21 at 22:24

1 Answers1

2

I might be understanding this wrong here, but from reading your question I assume ComponentLibrary looks something like this:

const ComponentLibrary = React.forwardRef((props, ref) => (
  <div ref={ref}>
    <div>
      <input type="text" />
    </div>
  </div>
));

I'm assuming this because the type prop of props.children has a value of

$$typeof: Symbol(react.forward_ref)

In replicating your MyCustomComponent setup

const MyCustomComponent = (props) => {
  const inputRef = useRef();

  useEffect(() => {
    console.log(inputRef.current); 
  });

  return React.cloneElement(props.children, {
    ref: inputRef,
  });
};

inputRef.current logs out the following element:

<div>
  <div>
    <input type="text" />
  </div>
</div>

You could use the children property to find a child element and chain this to eventually get the input element as you did in your example, but you could also use Element.querySelector and target the element by its attributes and/or type.

For example:

console.log(inputRef.current.querySelector('input[type="text"]'));
5eb
  • 14,798
  • 5
  • 21
  • 65
  • Much better solution. Thank you. I honestly didn't even think about doing this! – bruh Apr 19 '21 at 00:27
  • So, since the ComponentLibrary is a forwardedRef. Is there a better / preferred way of accessing it or it's children, instead of `props.children` ? – bruh Apr 19 '21 at 00:29
  • Also, thank you for bearing with me. I realize that was a lot of post, for a one line solution. But its extremely helpful that you broke down the various code blocks and either reiterated on the information I provided or gave context to the questions I asked. So cheers for your patience and detail – bruh Apr 19 '21 at 00:45
  • 1
    I'm not sure about a better way. You could use `children` if you use destructuring in the function arguments `({children})` instead of `(props)`, but that's a pretty insignificant change. You could also render `ComponentLibrary` inside `MyCustomComponent` directly without using props and attach the ref directly. You would lose some flexibility in terms of wrapping other similar types of components compared to use `children` though. For an overview of approaches of getting a dom node from child elements you could look at... – 5eb Apr 19 '21 at 16:48
  • 1
    ...[this](https://stackoverflow.com/questions/29568721/getting-dom-node-from-react-child-element?answertab=oldest#tab-top) – 5eb Apr 19 '21 at 16:48