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?
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?
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.
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} />;
}
}
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.
/*
* 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>
);
}
}
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')
);
First access the children with: this.props.children
, each child will then have its ref
as a property on it.
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
.
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>
)
}
}
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.
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;