94

If I have something like

<Parent>
  <Child1 />
  <Child2 />
  <Child3 />
</Parent>

And I want to access from Child2 where I have refs="child2refs", how can I do that?

Murmel
  • 5,402
  • 47
  • 53
user1354934
  • 8,139
  • 15
  • 50
  • 80
  • can you post your react code here? this.props.children should always word in this case... – omarjmh Jun 05 '16 at 21:47
  • For accessing child we can add ref to it and access it by this.refs.child. This will not work on connected component( a component that is connected to redux or other plugins ). we need to use getWrappedInstance() to get the wrapped instance and then we can access the state, refs and methods of that component. Here is the video explaining it - https://youtu.be/VpdKjocgCtA – Prem Nov 18 '17 at 06:19
  • 1
    There's now the possibility of forwarding refs: https://reactjs.org/docs/forwarding-refs.html – dude Jul 09 '18 at 11:30
  • @DaveF But when do you querySelector, and how soon will that element get replaced? Unreliable to say the least. – doug65536 Nov 11 '21 at 21:41

8 Answers8

100

Recommended for React versions prior to 16.3

If it cannot be avoided the suggested pattern extracted from the React docs would be:

import React, { Component } from 'react';

const Child = ({ setRef }) => <input type="text" ref={setRef} />;

class Parent extends Component {
    constructor(props) {
        super(props);
        this.setRef = this.setRef.bind(this);
    }

    componentDidMount() {
        // Calling a function on the Child DOM element
        this.childRef.focus();
    }

    setRef(input) {
        this.childRef = input;
    }

    render() {
        return <Child setRef={this.setRef} />
    }
}

The Parent forwards a function as prop bound to Parent's this. When React calls the Child's ref prop setRef it will assign the Child's ref to the Parent's childRef property.

Recommended for React >= 16.3

Ref forwarding is an opt-in feature that lets some components take a ref they receive, and pass it further down (in other words, “forward” it) to a child.

We create Components that forward their ref with React.forwardRef. The returned Component ref prop must be of the same type as the return type of React.createRef. Whenever React mounts the DOM node then property current of the ref created with React.createRef will point to the underlying DOM node.

import React from "react";

const LibraryButton = React.forwardRef((props, ref) => (
  <button ref={ref} {...props}>
    FancyButton
  </button>
));

class AutoFocus extends React.Component {
  constructor(props) {
    super(props);
    this.childRef = React.createRef();
    this.onClick = this.onClick.bind(this);
  }

  componentDidMount() {
    this.childRef.current.focus();
  }

  onClick() {
    console.log("fancy!");
  }

  render() {
    return <LibraryButton onClick={this.onClick} ref={this.childRef} />;
  }
}

Forwarding refs HOC example

Created Components are forwarding their ref to a child node.

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      const {forwardedRef, ...rest} = this.props;

      // Assign the custom prop "forwardedRef" as a ref
      return <Component ref={forwardedRef} {...rest} />;
    }
  }

  // Note the second param "ref" provided by React.forwardRef.
  // We can pass it along to LogProps as a regular prop, e.g. "forwardedRef"
  // And it can then be attached to the Component.
  return React.forwardRef((props, ref) => {
    return <LogProps {...props} forwardedRef={ref} />;
  });
}

See Forwarding Refs in React docs.

Rafi
  • 816
  • 9
  • 21
arpl
  • 3,505
  • 3
  • 18
  • 16
  • 58
    Passing a Ref down from the parent doesn't seem like the same thing as accessing a child ref from the parent – ESR Jul 02 '19 at 08:56
  • this is not the example I want. `LibraryButton` is not a `class`. Could you show example in `class LibraryButton extends React.Component`? Thank you. – new2cpp Jul 24 '19 at 05:26
  • In the case of forwarding refs in HOCs and connected components, I think it is helpful to mention the legacy way of doing this was to use the options parameter on the connect method `connect(mapStateToProps, null, null, { withRef: true })(Cmp);` and accessing the wrapped component instance in, for example, an HOC with the `this.wrappedComponentRef.current.getWrappedInstance()`. See the section on **Using refs on connected components** [here](https://itnext.io/advanced-react-redux-techniques-how-to-use-refs-on-connected-components-e27b55c06e34#63a4) – rdrw Sep 05 '19 at 16:30
22
  1. Inside the child component add a ref to the node you need
  2. Inside the parent component add a ref to the child component.
/*
* Child component
*/
class Child extends React.Component {
  render() {
    return (
      <div id="child">
        <h1 ref={(node) => { this.heading = node; }}>
          Child
        </h1>
      </div>
    );
  }
}

/*
 * Parent component
 */
class Parent extends React.Component {
  componentDidMount() {
    // Access child component refs via parent component instance like this
    console.log(this.child.heading.getDOMNode());
  }

  render() {
    return (
      <div>
        <Child
          ref={(node) => { this.child = node; }}
        />
      </div>
    );
  }
}

Demo: https://codepen.io/itsfadnis/pen/aLWVVx?editors=0011

Nikhil Fadnis
  • 850
  • 6
  • 8
18

Here is an example that will focus on an input using refs (tested in React 16.8.6):

The Child component:

class Child extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return (<input type="text" ref={this.myRef} />);
  }
}

The Parent component with the Child component inside:

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.childRef = React.createRef();
  }
  componentDidMount() {
    this.childRef.current.myRef.current.focus();
  }
  render() {
    return <Child ref={this.childRef} />;
  }
}

ReactDOM.render(
    <Parent />,
    document.getElementById('container')
);

The Parent component with this.props.children:

class Parent extends React.Component {
    constructor(props) {
        super(props);
        this.childRef = React.createRef();
    }
    componentDidMount() {
        this.childRef.current.myRef.current.focus();
    }
    render() {
        const ChildComponentWithRef = React.forwardRef((props, ref) =>
            React.cloneElement(this.props.children, {
                ...props,
                ref
            })
        );
        return <ChildComponentWithRef ref={this.childRef} />
    }
}

ReactDOM.render(
    <Parent>
        <Child />
    </Parent>,
    document.getElementById('container')
);
narmageddon
  • 1,117
  • 2
  • 10
  • 7
16

First access the children with: this.props.children, each child will then have its ref as a property on it.

omarjmh
  • 13,632
  • 6
  • 34
  • 42
9

If everything you have is props.children:

const Parent = (p: {children: JSX.Element}) => {
    const childRef = useRef()

    return React.cloneElement(p.children, { ref: childRef })
}
<Parent>
  <SingleChild />
</Parent>

Note that it will fail if your child cannot have a ref, e.g. React.Fragment.

Monsignor
  • 2,671
  • 1
  • 36
  • 34
  • Works perfectly. According to React docs, cloneElement has 'pitfalls', but I don't know how else to do this. https://react.dev/reference/react/cloneElement#alternatives – Richard Lovejoy May 17 '23 at 02:31
2

I think this guide explains it pretty well https://github.com/yannickcr/eslint-plugin-react/issues/678

class Field extends Component {
  const { inputRef } = this.props;
  render() {
    return (
      <input type="text" ref={inputRef} />
    )
  }
}

class MyComponent extends Component {
  componentDidMount() {
    this.inputNode.focus();
  }

  render() {
    return (
      <div>
        Hello, <Field inputRef={node => this.inputNode = node} />
      </div>
    )
  }
}
OZZIE
  • 6,609
  • 7
  • 55
  • 59
2

Here is how I solve the problem for dynamic components:

On the parent, dynamically create references to the child components, for example:

class Form extends Component {
    fieldRefs: [];

    // dynamically create the child references on mount/init
    componentWillMount = () => {
        this.fieldRefs = [];
        for(let f of this.props.children) {
            if (f && f.type.name == 'FormField') {
                f.ref = createRef();
                this.fieldRefs.push(f);
            }
        }
    }

    // used later to retrieve values of the dynamic children refs
    public getFields = () => {
        let data = {};

        for(let r of this.fieldRefs) {
            let f = r.ref.current;
            data[f.props.id] = f.field.current.value;
        }

        return data;
    }
}

The Child component (ie <FormField />) implements it's own 'field' ref, to be referred to from the parent:

class FormField extends Component {
    field = createRef();
    
    render() {
        return(
            <input ref={this.field} type={type} />
        );
    }
}

Then in your main page, the "parent's parent" component, you can get the field values from the reference with:

class Page extends Component {
    form = createRef();

    onSubmit = () => {
        let fields = this.form.current.getFields();
    }

    render() {
        return (
            <Form ref={this.form}>
                <FormField id="email" type="email" autoComplete="email" label="E-mail" />
                <FormField id="password" type="password" autoComplete="password" label="Password" />

                <div class="button" onClick={this.onSubmit}>Submit</div>
            </Form>
        );
    }
}

I implemented this because I wanted to encapsulate all generic form functionality from a main <Form /> component, and the only way to be able to have the main client/page component set and style its own inner components was to use child components (ie. <FormField /> items within the parent <Form />, which is inside some other <Page /> component).

So, while some might consider this a hack, it's just as hackey as React's attempts to block the actual 'ref' from any parent, which I think is a ridiculous design, however they want to rationalize it.

Ryan Weiss
  • 1,308
  • 1
  • 14
  • 36
  • Consider moving the React bashing to the bottom of the answer instead of leading off with it. – CodeFinity Mar 05 '21 at 18:25
  • Thanks. I already did, even considered removing it. Then, I forgot. Now that you mention it, I think I will leave it so that only those who can read through it, understand the concerns, and still make it to the solution, might actually warrant worthy usage out of it. Maybe you have some comments on the integrity of the solution? Cheers. – Ryan Weiss Mar 06 '21 at 03:32
  • Solution is fine. It depends on your intentions as it's your answer, but if folks are on here to focus on getting code solutions expeditiously, then the lead in you have deters from that one. – CodeFinity Mar 07 '21 at 21:34
  • 1
    @CodeFinity Point taken and entry edited. Thanks. – Ryan Weiss Mar 09 '21 at 14:59
0

If you want to get the ref of children when in the parent component

// children is an array
const updatedChildren = Children.map(children, (child) => {
  return { ...child.props, ref: child.ref };
}),

// children is not an array
const childrenRef = children.ref;
Alessia
  • 899
  • 10
  • 16