ReactJs How to access child components refs from parent - javascript

How to access the refs of children in a parent to do something with them in the parent function?
class Parent extends Component {
someFunction(){
// how to access h1 element of child in here ??
}
render() {
return (
<Child />
);
}
}
class Child extends Component {
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}

To add to Shubham's answer, the child refs have to be accessed inside of componentDidMount() inside parent. Something like:
class Parent extends React.Component {
componentDidMount(){
var elem1 = this.refs.child1.refs.childRefName;
}
return (
<View>
<Child1 ref='child1'/>
<Child2 />
<Child3 />
</View>
);
}

You can access the child refs by providing a ref to the child element and accessing it like ReactDOM.findDOMNode(this.refs.child.refs.hello)
In your case the child component doesn't begin with Uppercase letter which you need to change.
class App extends React.Component {
componentDidMount() {
console.log(ReactDOM.findDOMNode(this.refs.child.refs.hello));
}
render() {
return (
<Child ref="child"/>
);
}
}
class Child extends React.Component {
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<divi id="app"></div>

You can also use React.forwardingRef method to make the Child component able to receive and define a ref from its parent.
Here is the documentation for the method:
https://reactjs.org/docs/forwarding-refs.html
And here is an example of how you might implement it in your code:
const Child = React.forwardRef((_, ref) => (
<h1 ref={ref}>Child Component</h1>
));
function Parent() {
var h1 = React.createRef();
React.useEffect(() => {
console.log(h1.current);
});
return <Child ref={h1} />;
}
https://reactjs.org/docs/forwarding-refs.html
I hope it helps.

Related

ReactJS: Is this good way to call siblings method?

I am doing this to call siblings method (state) this way:
App.js
class App extends Components {
onClick(){
/*.. calulate total..*/
this.setState({
total: window.totalValue
})
}
render() {
return (
<Main>
{/*..*/}
<Body onClick={this.onClick}/>
<Footer total={this.state.total} />
</Main>
);
}
}
class Body extends Components {
onClick(){
this.props.onClick();
}
render(){
return <Section onClick={this.onClick} />
}
}
class Section extends Components{
render(){
return (
<button onClick={props.onClick}>Calculate</button>
)
}
}
class Footer extends Components{
render(){
return props.total;
}
}
But its hard to keep passing props to every child components when there are more. Is it okay to do something like this?
Manager.js
const Components = {}
export const connect = ( obj ) =>{
Components[obj.constructor.name] = obj;
return obj;
}
export const manager = () => {
return Components;
}
And in <Footer/>'s constructor pass this to connect function to save its reference:
/*...*/
constructor(props){
super(props)
connect(this) // saving component reference to call its method later.
}
/*...*/
And in <Section/>, add onClick and in <Footer/> add calculateTotal methods like this:
/*.. Section ..*/
onClick(){
manager().Footer.calculateTotal();
}
/*.. Footer ..*/
calculateTotal(){
this.setState({
total: window.totalValue
})
}
render(){
return this.state.total;
}
Later approaches just set the state of <Footer/> instead of passing value form <Main/> and we don't have to call every onClick method (state) when deepest child's state is called.
Thanks in advance for your answer.

call child function from parent in react 16

After upgrading to react 16 I am getting null in console.log(this.child)
My parent component
import EditReview from './partials/editReview'
class VenueDetails extends Component {
constructor(props) {
super(props)
this.child = React.createRef();
}
editButtonClick = () => {
console.log(this.child)
this.child.current.onEditClick()
}
render() {
return (
<div>
<button className="pull-right" onClick={() => this.editButtonClick(review, i)}>edit</button>
<div className="place-review-text">
<EditReview {...this.props}/>
</div>
</div>
)
}
}
My child component
class EditReview extends Component {
onEditClick(review, editIndex) {
console.log('ppp')
}
render() {
return ()
}
}
export default EditReview
I need to call onEditClick from the parent component. I tried this but doesn't work.
Kindly help me
You have to assign the ref:
<EditReview {...this.props} ref={this.child} />
Also, you don't need to use inline arrow function:
onClick={() => this.editButtonClick(review, i)}
// ------^^^^^ not required
// because, you're already using public class method
Just use:
onClick={this.editButtonClick(review, i)}
Define your method like this:
editButtonClick = (review, index) => { // to access review, i

How can I wrap the Children of a Parent component in a HOC and render them in React?

Let me start by saying that this example is very simple and can be solved with React.cloneElement. But I want more freedom and the project will be more complex, so I'd like to find a solution.
I would also like to understand what I'm missing :/
I want to be able to augment the children of a Parent component with props and methods (hence the HOC). It would start from here:
<Parent>
<aChild />
<anotherChild />
<yetAnotherChild />
</Parent>
And this is the Parent component (called Sequence in my project), so far:
import React, { Component } from 'react';
const withNotification = handler => Component => props => (
<Component onAnimationEnd={handler} {...props} />
);
class Sequence extends Component {
constructor(props) {
super(props);
this.state = {
pointer: 0,
};
this.notifyAnimationEnd = this.notifyAnimationEnd.bind(this);
this.Children = React.Children.map(this.props.children, Child =>
withNotification(this.notifyAnimationEnd)(Child)
);
}
notifyAnimationEnd() {
// do stuff
}
render() {
return (
<div>
{this.Children.map((Child, i) => {
if (i <= this.state.pointer) return <Child />;
return <div>nope</div>;
})}
</div>
);
}
}
export default Sequence;
I get the following error:
You can play with the code here: https://codesandbox.io/s/6w1n5wor9w
Thank you for any help!
This answer will not solve your problem but maybe gives a hint why this is not possible. At first I was surprised why your code does not work, even though I'm not an experienced React developer it seems ok map this.props.children through with React.Children.map and return the desired Component with your HOC. But it did not work.
I tried to debug it a little bit and did some search. I've learned props.children actually contains the elements itself not the instances of components. Even, React.Children.map does not have any effect on this.
Here is a working snippet proves that your problem is not related with the HOC. I've used an array of components instead of mapping through props.children.
class Line1 extends React.Component {
componentDidMount() {
setTimeout(this.props.onAnimationEnd, 1000);
}
render() {
return <div>Line 1</div>;
}
}
class Line2 extends React.Component {
componentDidMount() {
setTimeout(this.props.onAnimationEnd, 1000);
}
render() {
return <div>Line 2</div>;
}
}
class Line3 extends React.Component {
componentDidMount() {
setTimeout(this.props.onAnimationEnd, 1000);
}
render() {
return <div>Line 3</div>;
}
}
const withNotification = handler => Component => props => (
<Component onAnimationEnd={handler} {...props} />
);
class Sequence extends React.Component {
constructor(props) {
super(props);
this.state = {
pointer: 0
};
this.notifyAnimationEnd = this.notifyAnimationEnd.bind(this);
this.Arr = [ Line1, Line2, Line3 ];
this.Children = this.Arr.map(Child =>
withNotification(this.notifyAnimationEnd)(Child)
);
}
notifyAnimationEnd() {
this.next();
}
next() {
// Clearly, render the next element only if there is, a next element
if (this.state.pointer >= this.Arr.length - 1) {
return;
}
this.setState({ pointer: this.state.pointer + 1 });
}
render() {
return (
<div>
{this.Children.map((Child, i) => {
if (i <= this.state.pointer) return <Child />;
return <div>nope</div>;
})}
</div>
);
}
}
ReactDOM.render(
<Sequence />,
document.getElementById("app")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
You are returning <Child /> instead of Child in Sequence.js render method. Here is my edited copy - codesandbox

Call function in parent and bind `this` to the child?

What I'm trying to do is call a function defined in a parent element from its child, but bind this to the child calling the function, rather than the parent where the function runs.
Is there a way to do this and would this be an anti-pattern if so? Thank you.
Parent Function to Pass to Child
onSelect = (event => {
// Some code where `this` is the scope of the child calling the function
// Not the scope of the parent where the function is defined
}
Parent Render Function
render() {
return (
<Child onSelect={this.onSelect} />
)
}
Child Render Function
render() {
return (
<button onClick={this.props.onSelect.bind(this)} />
)
}
The problem is you're defining onSelect as an arrow function, so it closes over this rather than using the this it was called with. Just make it a method or non-arrow function:
class Parent extends React.Component {
onSelect() {
console.log(this.constructor.name);
console.log(this.note);
}
render() {
return <Child onSelect={this.onSelect} />;
}
}
class Child extends React.Component {
constructor(...args) {
super(...args);
this.note = "I'm the child";
}
render() {
return (
<button onClick={this.props.onSelect.bind(this)}>Click Me</button>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
But you might consider binding onSelect once rather than repeatedly (e.g., not in render), perhaps in Child's constructor. But it really only matters if render will get called a lot. E.g.:
class Parent extends React.Component {
onSelect() {
console.log(this.constructor.name);
console.log(this.note);
}
render() {
return <Child onSelect={this.onSelect} />;
}
}
class Child extends React.Component {
constructor(...args) {
super(...args);
this.note = "I'm the child";
this.onSelect = this.props.onSelect.bind(this);
}
render() {
return (
<button onClick={this.onSelect}>Click Me</button>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Is there a way to do this and would this be an anti-pattern if so?
Pass a function (not an arrow function), and bind it in the constructor.
It's an anti pattern because the parent needs to be aware of the inner working of the child, and this breaks encapsulation.
How do you do that:
Use a standard method, and not an arrow function:
onSelect(e) {
this.setState({
selected: !!e.target.value
});
}
Bind the method in the constructor:
constructor(props) {
super(props);
this.state = {
selected: false
};
this.onSelect = this.props.onSelect.bind(this);
}
Working example:
const { Component } = React;
class Child extends Component {
constructor(props) {
super(props);
this.state = {
selected: false
};
this.onSelect = this.props.onSelect.bind(this);
}
render() {
const {selected} = this.state;
return (
<div>
<input onSelect={this.onSelect} defaultValue="I'm the text" />
<div>{selected ? 'selected' : 'not selected'}</div>
</div>
);
}
}
class Parent extends Component {
onSelect(e) {
this.setState({
selected: !!e.target.value
});
}
render() {
return (
<Child onSelect={this.onSelect} />
)
}
}
ReactDOM.render(
<Parent />,
demo
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="demo"></div>

React - Instance creation and usage of a class

I'm trying to get an instance of the class ActionEditor So that I'd be able to use its methods later:
function render() {
const toRender = responseActions.map((actionInstance) => {
currentActionEditing=actionInstance;
return <li>{ actionInstance === expandedAction ? <ActionEditor id={actionInstance.title} action={getActionByKey(actionInstance.actionType)} instance={actionInstance} state={actionInstance} /> : <button onClick={createOnClick(actionInstance)}>{actionInstance.title}</button>}</li>;
});
ReactDOM.render(
<div>
<div>{toRender}</div>
<button style={styleButtonGenerate} onClick={onGenerateClick}>Generate</button>
</div>,
document.getElementById('root')
);
}
I've attempted to use it through an onClick method like so:
function onGenerateClick() {
var editor = document.getElementById(currentActionEditing.title);
editor.prototype = ActionEditor;
editor.methodIWantToUse();
}
But it always turns out to be null/undefined.
I understand that it's not the best example but it should be enough to demonstrate the issue.
Is there a way around this?
I think what you want here is to save a ref to the component so it can be accessed, see in the example below how the sayHi method is called from the parent component.
class MyComponent extends React.Component {
sayHi() {
console.log('hi');
}
render() {
return (<div>I'm a component!</div>)
}
}
class App extends React.Component {
render() {
// just a way to show how to access a child component method.
setTimeout(() => {
this.node.sayHi();
}, 1000)
return (<MyComponent ref={(node) => this.node = node}/>)
}
}
ReactDOM.render(<App />, document.querySelector("body"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Categories

Resources