Reputation: 1109
Is there any way to get access to the child components nested inside a react class component? I know React.children lets you access the children of a component, but my understanding is that this doesn't work if the children are instantiated in the render method. For example, say you have the following React class components:
class Child extends React.Component {
sayHi(){console.log('child ' + this.props.id + ' says hi')}
render() {
return <h1>ID: {this.props.id}</h1>;
}
}
class Parent extends React.Component {
render() {
return <div>
<Child id="A" />
<Child id="B" />
<Child id="C" />
</div>
}
}
If you instantiate a new Parent object, is there any way to access the Child objects inside of it in order to call the method belonging to a specific child?
Thanks in advance.
Upvotes: 3
Views: 10438
Reputation: 1342
That's absolutly possible (check this codesandbox) but we should be sure that we need that pattern, here is what the react doc sad about when we really should use the refs on the parent to control children components: (link to the docs):
When to Use Refs:
Avoid using refs for anything that can be done declaratively.
For example, instead of exposing open() and close() methods on a Dialog component, pass an isOpen prop to it.
here is an example of how to use it:
Parent component:
import React, { Component, createRef } from 'react';
import Child from './Child';
const childrenData = [
{
name: 'child 1',
ref: createRef(),
},
{
name: 'child 2',
ref: createRef(),
},
{
name: 'child 3',
ref: createRef(),
},
];
class Parent extends Component {
componentDidMount() {
const [{ ref: firstChildRef }] = childrenData;
firstChildRef.current.toggle();
}
render() {
return childrenData.map((props) => <Child key={props.name} {...props} />);
}
}
export default Parent;
Child component:
import React, { Component, forwardRef } from 'react';
const childStyle = {
border: '2px solid gray',
padding: 5,
margin: 5,
};
class Child extends Component {
state = { enabled: true };
toggle = () => {
setTimeout(() => {
this.setState(({ enabled }) => ({
enabled: !enabled,
}));
}, 500);
};
render() {
const { enabled } = this.state;
const background = enabled ? 'lime' : 'red';
return (
<div style={{ ...childStyle, background }}>
<h3>{this.props.name}</h3>
state {enabled ? 'enabled' : 'desabled'}
</div>
);
}
}
export default forwardRef((props, ref) => <Child ref={ref} {...props} />);
Upvotes: 4
Reputation: 3373
You would use React refs to create and store references to the child components. These references have all methods of a normal DOM element, plus you can call any public method on the React component class that created them.
class Child extends React.Component {
sayHi() {
console.log('child ' + this.props.id + ' says hi')
}
render() {
return <h1>ID: {this.props.id}</h1>;
}
}
class Parent extends React.Component {
constructor(props) {
this.child_1 = React.createRef();
this.some_event_did_happen = this.some_event_did_happen.bind(this);
}
some_event_did_happen() {
if (this.child_1) {
this.child_1.sayHi();
}
}
render() {
return <div>
<Child id="A" ref={this.child_1} />
<Child id="B" />
<Child id="C" />
</div>
}
}
Note that I do not advocate the use of React refs for this purpose. It's better to pass around an EventEmitter, and then use normal React state management methods to change your application state. However, React refs are the quick-and-dirty solution.
Upvotes: 1