-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to call child component method from parent? #909
Comments
For example you can use Refs to Components approach like so: /* Child.js */
import React from 'react'
import withStyles from 'isomorphic-style-loader/lib/withStyles'
import s from './Child.css'
class Child extends React.Component {
componentDidMount() {
this.props.onRef(this)
}
componentWillUnmount() {
this.props.onRef(undefined)
}
method() {
window.alert('do stuff')
}
render() {
return <h1 className={s.root}>Hello World!</h1>
}
}
export default withStyles(s)(Child); /* Parent.js */
import React from 'react'
import Child from './Child'
class Parent extends React.Component {
onClick = () => {
this.child.method() // do stuff
}
render() {
return (
<div>
<Child onRef={ref => (this.child = ref)} />
<button onClick={this.onClick}>Child.method()</button>
</div>
);
}
} |
@frenzzy it works! tks! |
Hello @frenzzy , first I want to thank you for amazing solution. It worked like charm, but could you please explain me what this:
is exactly doing? I am new in this so I would like to know why it works? :D Thank you very much |
When you call the onRef function inside Child-component, you are calling the function passed from the parent component. for example:
|
@Sartonon Thank you very much for nice explanation! |
@Sartonon |
@Vamshikrishna209 you can save references like so: <div>
{this.props.array.map((item, index) =>
<ChildComponent onRef={ref => (this[`example${index}`] = ref)} />
)]
</div> and then get access to it this.example0.childComponentMethod();
// or
this[`example${index}`].childComponentMethod(); |
This is antipattern! |
I just randomly found this because I want to write unit tests for component functions but to anyone coming here in the future: @langpavel is right, do not do this! So define the method on the parent, pass it to the child as prop and call it in the child; class Thing extends Component {
render () {
return (
<View>
{getThings()}
</View>
)
}
getThings () {
const thingNodes = []
for (let thing of this.props.things) {
thingNodes.push(<Child key={thing.id} name={thing.name} doStuff={this.doStuff} />)
}
return thingNodes
}
doStuff () {
console.log(this.props.name) // thing.name as passed in getThings as this function is not bound to its defining class.
}
}
class Child extends Component {
render () {
return (
<Button onClick={this.props.doStuff}>
{this.props.name}
</Button>
}
}
(but binding to |
@langpavel How should the app be structured so as to avoid this design pattern, if the parent must call a child function so as to avoid code bloat. |
@dmr07 If you need call child function, it indicates lack of data model in your app. |
@langpavel why is this an anitpattern ? |
But how to set ref to
I want to call
Any idea? |
It does not work like that. Restructure your data model as said before like so: import React, {Component} from 'react'
import Child from './child.js'
class Parent extends Component {
constructor (props) {
super(props)
this.state = {
item: ''
}
}
render () {
<div>
<Child setItem={item => this.setState({item})} item={this.state.item} />
</div>
}
} if this becomes too complex use Redux or Mobx. If there is part of the functionality in onClick thet needs to be defined on the child, just give it a callback: constructor (props) {
super(props)
this.onClick = this.onClick.bind(this)
}
onClick (callback, state) {
// do things
callback()
this.setState(state)
} (But first think about the data model and code structure. It is probably not needed. I only call state changing functions on children and stuff just happens because of a re-render.) |
@ThaJay No, that is not what I want, Child need to be dynamic . Parent only need the function inside Child , but shouldn't hardcode Child inside it. For example:
I have worked it around by using
Detail see my question: |
Perhaps something like this? index.js import React from 'react'
class Parent extends React.Component {
handleSubmit1 () {}
handleSubmit2 () {}
render () {
return (
<div>
<Dialog visible={this.state.dialog1Visible} onClick={this.handleSubmit1}>
<DialogChild1 />
</Dialog>
<Dialog visible={this.state.dialog2Visible} onClick={this.handleSubmit2}>
<DialogChild2 />
</Dialog>
</div>
// button is in Dialog component
)
}
} Get creative with your architecture, there surely is a way to structure the code better. Or you haven't explained in enough detail why it it needed. But as react-starter-kit is a beginner package I assume the first. |
I did not read your point properly. You want to pass child from higher up. See the following example. Of course you can use import React, {Component} from 'react'
import constants from './constants.js'
import children from './children.js'
import submitters from './submitters.js'
class Parent extends Component {
constructor(props) {
super(props)
this.state = {
child :'',
onSubmit:''
}
}
componentWillMount () {
const name = constants[this.props.childName]
const child = children[name]
const onSubmit = submitters[name]
this.setState({child, onSubmit})
}
render () {
return (
<div>
<Dialog onSubmit={this.state.onSubmit}>
{this.state.child}
</Dialog>
</div>
)
}
} A child does not have submit logic. |
I have a parent that needs to call its child component every time a scroll event triggers for custom lazy loading. I have it set up so that when the parent is scrolled, setState toggles a boolean. The child receives this with componentWillReceiveProps, and triggers the child to check its position in the viewport. Problem is that of course, each received prop triggers a re-render. About 1000 of them. I've debounced it, but am looking for a more elegant solution. I came here looking to circumvent updating state, hoping that the parent could simply tell the child to check its position on scroll, thereby avoiding re-render if not necessary. Is using refs still a bad idea in this case? |
I would not bother thinking about the logic and just use a module. A quick google gave me the following: |
Thanks, your method is very similar to the delegates in the native ios |
@frenzzy How can you make HOC out of that logic? So always when I want to get ref I don't have to add those |
@henrikra you don't need HOC for this, example: import React from 'react'
class Child extends React.Component {
render() {
return <label>Input: <input ref={this.props.inputRef} /></label>
}
}
class Parent extends React.Component {
componentDidMount() {
this.input.focus() // <= access to child ref
}
render() {
return <Child inputRef={ref => this.input = ref} />
}
} |
Ok, I don't understand all the people who think this is a structural problem and not needed. Situation: You have a FEW DIFFERENT parent components which both contain the same child component. The child component is Alerts (which contains all the Alerts at the top of the page). The same methods will need to be called within the child components. The child component will always need to have an addAlert and removeAlert function. These methods are called through the form submits of the parent component. Wouldn't it be nice if you would not have to re-write the methods passed down to the child components? Doesn't seem very DRY if you can't. |
Using an imperative handler approach:
|
Today I find next solution
VideoBlock component has next render method
After this preparations, I can simply invoke child's method with next syntax:
|
I don't fully agree with things in this conversation and I have no idea how to get it to change in the React world of design patters. Calling methods on a child component should not be an anti-pattern or considered a bad pattern. It should be encouraged given certain use cases. For example: I have a silly game app and all you have to do is drag little circles from one side of the screen to the other. Sometimes there can be over 200 of them at once on screen. Many MANY times I need to know the positions and a number of state values in my child components of these circles. Also, many other times, I need the parent (which is the game itself) to not only house all of the logic of the game, which the circles should NOT know, but also update things based on it's rules. Often this involves changing the state of one of the components... To say that I should be putting over 200 items and their states into Redux or application state is total poppycock! Application state is supposed to be simple, clean, derivable objects and not to go crazy with because of badly things can get slowed down if you keep choosing to just casually throw everything into app state. Now imagine as I drag them, that I need to update their state through redux every pixel! Not only am I updating a massive state object consistently but I am also causing render logic to have to ripple down through the components using this state... Why not have the ability for the parent to call a clean, concise method to access it's own component state safely? It would be clean because the component owns it's own state changing functions and can allow/disallow clean/dirty choices of access. |
the desire to use an antipatern usually identifies a shortcoming in the framework |
how can I pass a 'ref' to Child Component from Parent Component?
|
I found a reasonably sensible solution to this problem by 'brickingup' on StackOverflow. The idea is that the parent passes a callback function to the child, and the child calls that function, to pass its method(s) to the parent. The parent can then store the methods locally. The important code is commented below. class Parent extends Component {
acceptMethods(childDoAlert) {
// Parent stores the method that the child passed
this.childDoAlert = childDoAlert;
}
render() {
return (
<div>
{/* Give the child a callback, so it can share its methods with us. */}
<Child shareMethods={this.acceptMethods.bind(this)}/>
{/* Later we can call the child's method directly! */}
<button onClick={() => this.childDoAlert()}>Click</button>
</div>
);
}
}
class Child extends Component {
componentDidMount() {
// Child passes its method to the parent
this.props.shareMethods(this.doAlert.bind(this));
}
doAlert() {
alert('clicked');
}
render() {
return (
<h1>Hello</h1>
);
}
} If you want to receive more than one method from the child, then change For clarity, I think the |
With a little tweak of @frenzzy's solution, I was able to get it to work with "withStyles" higher order component from isomorphic-style-loader |
I realize this issue is closed. The ONLY reason I'm replying to it is because it's now the #1 Google search result for
Many of the suggestions above for working around this method are useful to know. I don't personally agree that a much longer solution or library dependency to avoid a 2-line option is objectively "better", or that this option (direct ref-taking) is indeed even a true "antipattern," but that's my personal opinion. I respect the authors of this starter kit, their work, and their goals for how it's meant to be used. My comments here apply only to those looking for this solution generally (particularly in React Native, as I was before I went on this fact-checking goose chase), NOT in the context of React Starter Kit. |
Let me add some more advice to the pour soul that might be having some issue with the referencing of child component.
I lost almost 2 days before realizing that. After i removed the react-loadable (the npm package that makes any react component lazy loadable), the referencing worked nicely like mentioned by several people above |
Based on @joeytwiddle solution, i've implemented this another solution, who have worked very fine. class Parent extends Component {
render() {
return (
<div>
{/* Give the child a callback, so it can share its methods with us. */}
{React.cloneElement(this.props.children, {onChildDoAlert: _handleChildDoAlert => {this.childDoAlert = _handleChildDoAlert}})}
{/* We can now call the child's method directly! */}
<button onClick={this.childDoAlert}>Click</button>
</div>
);
}
}
class Child extends Component {
componentDidMount() {
// Child passes its method to the parent
this.props.onChildDoAlert(this.handleDoAlert.bind(this))
// or you can pass the implementation directly
// this.props.onChildDoAlert(() => {
// alert('clicked')
// })
}
handleDoAlert() {
alert('clicked');
}
render() {
return (
<h1>Hello</h1>
);
}
} |
Here is an example why I think this is not an antipattern: You have a panel component that can be in an "folded" or "unfolded" state. The component includes a button to fold\unfold. Now you have a parent component with several of such panels. You have a buttons in parent "fold all" and "unfold all" which cause all the accordions to fold\unfold. You could pass a "open=false" as a prop. The problem is now you have two sources of truth: the parent's (which is passed as a prop) and the child's (which is changed with setState on every click) What you might do is make the parent the single source of truth, and on every click of the child call "onClick" callback and have the parent modify the state. That might work but is not intuitive. Why should the child tell the parent it was clicked just so the parent can tell it again "yes you were clicked" using a prop? Intuitively the child owns the state here. And intuitive app is more readable and understandable. On the other hand changing the child's state using a method conveys the message: the child owns the state here, and it allows others to ask him to change. |
I have not read everything but I was facing the same issue. Instead of using refs. I used callback. I had to reset the form fields once the container has submitted the form. Child: validateFields((err, values) => {
// handleSubmit is from the parent (props)
handleSubmit(err, values, () => {
form.resetFields();
});
}); Parent: handleSubmit = (err, values, resetFields) => {
// my code
resetFields();
} Anti-Pattern? |
@langpavel - Your explanation of why this is an anti-pattern, and suggesting to use Redux is not satisfactory. React should handle such basic needs without the needs of an external independent tool such as Redux. Components are instances of javascript classes (or functions) and there should be a "reactive" way to simply access the instances' methods from the parent, in React. For example:I have I believe passing I ended up with a Back in the good days, in vanilla JS, I could easily create an instance of a timer from a parent, and render it using my vanilla framework, and from the parent component loop on all the child instances which are timers and call each of those Why, for such a simple app (described above), would I need Redux or RXJX for? it's insane. |
@yairEO The issue is less that using refs is an anti-pattern and more that refs are just unnecessarily difficult to work with. You can write 2-3 lines of code and pass a silly callback or you can write 15-20 lines of code to track the refs and call the child function. This gets super annoying when your adding/removing elements. Imagine you were writing a dashboard with custom blocks that could be customized, but had to refresh every 30 seconds to update their data. However, you also needed a manual refresh button. What if that refresh button could also be moved around the page? Trying to track all the refs down into the dashboard would be a nightmare. It may be simple enough, but if you allow components to be nested then... 💥. This is very similar to your use case with the timers and it really shows where React falls short. It does not provide a built-in observer pattern. I would actually argue that the callback method is the anti-pattern since its a convoluted replacement of observer pattern. Ultimately, the issue is that React doesn't provide a way to fire/listen-to custom events which is something that even simple apps have the need for very quickly if you don't want to re-glue all your components every time you move something around on the page. What your really looking for is something like this: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events or something like what Vue does https://vuejs.org/v2/guide/components-custom-events.html. How this works is to fire a "refresh" event and observe it with the individual timers who are responsible for handling the event appropriately. Which is slightly more code than the callback method but
|
Another solution: In all seriousness, thanks Frenzzy for the great solution! I'm looking for a way to solve a very simple problem: I have a button in the parent container, and I have a menu that needs to be toggled in a child component once that parent button is clicked. For this I'll need to pass some sort of data down to the child, or in this solution, just call the child listener function to toggle the Boolean state property. Or another simpler way of putting it: I'm making a website navigation bar. (When you click the hamburger icon on a mobile device, and the menu drops down). |
We use a custom hook called useCounterKey to send events to children. Although it's not as straightforward as e.g. a simple HTML event handler, it's less messy than using refs (the data flow is only one-way) and is pretty React-idiomatic (since incrementing keys can already be used to reset components). Details here on how we're using it: https://stackoverflow.com/a/60568459/152711 |
Thanks!!! |
Example with functional components and useRef hook: /* Child.jsx */ import React, { useEffect } from "react";
import withStyles from "isomorphic-style-loader/lib/withStyles";
import s from "./Child.css";
const Child = () => {
useEffect(() => {
onRef(() => {
console.log("lol");
});
}, []);
return <h1 className={s.root}>Hello World!</h1>;
};
export default withStyles(s)(Child); /* Parent.jsx */ import React, { useRef } from "react";
import Child from "./Child";
const callbackRef = useRef();
const Parent = () => {
return (
<div>
<Child
onRef={(callback) => {
callbackRef.current = callback;
}}
/>
<button
onClick={() => {
callbackRef && callbackRef.current();
}}
>
Child.method()
</button>
</div>
);
}; |
The answer from @dfyz011 works but not in TypeScript, here's the TS option, hope it helps 😄 /* Child.tsx */ import React, { useEffect } from "react";
type Props = {
onRef: (callback: Function) => void;
}
export const Child: React.FC<Props> ({onRef}) => {
useEffect(() => {
onRef(() => {
console.log("Dude, see? It's working!");
});
}, [onRef]);
return <h1 className={s.root}>Hello World!</h1>;
}; /* Parent.tsx */ import React, { useRef } from "react";
import Child from "./Child";
export const Parent = () => {
const callbackRef = useRef<Function>();
const handleOnRef = (callback: Function) => {
callbackRef.current = callback;
};
return (
<div>
<Child onRef={handleOnRef} />
<button
onClick={() => {
callbackRef?.current?.();
}}
>
Child.method()
</button>
</div>
);
}; |
Child.js
Parent.js
The text was updated successfully, but these errors were encountered: