Pottercomuneo
Pottercomuneo

Reputation: 787

Call child function from parent component in React Native

I'm developing my first React Native app. What I'm trying to achieve is to execute a child function from the parent component, this is the situation:

//Child

    export default class Child extends Component {
      ...
      myfunct: function() {
        console.log('Managed!');
      }
      ...
      render(){
        return(
          <Listview
          ...
          />
        );
      }
    }

//Parent

    export default class Parent extends Component {
      ...
      execChildFunct: function() {
        ...
        //launch child function "myfunct"
        ...
        //do other stuff
      }
      
      render(){
        return(
          <View>
            <Button onPress={this.execChildFunct} />
            <Child {...this.props} />
          </View>);
      }
    }

In this example, I would like to log 'Managed!' when I press the button in the parent class. How is it feasible?

Upvotes: 52

Views: 61944

Answers (7)

gshoanganh
gshoanganh

Reputation: 335

Child Component:

    /* Child.js */
    import React from 'react'
    
    class Child extends React.Component {
      componentDidMount() {
        this.props.onRef(this) //-> important 
      }
      componentWillUnmount() {
        this.props.onRef(undefined)
      }
      onChange() {
        alert('ok success')
      }
      render() {
        return <View><Text>Hello World!</Text><View>
      }
    }
    
    export default (Child);

Parent Component:

    /* Parent.js */
    import React from 'react'
    import Child from './Child'
    
    class Parent extends React.Component {
      onShow = () => {
        this.child.onChange()
      }
      render() {
        return (
          <View>
            <Child onRef={ref => (this.child = ref)} />  **important**
            <Button onPress={()=>this.onShow()} title={"button check"}/>
          </View>
        );
      }
    }

Upvotes: 0

Burak Karasoy
Burak Karasoy

Reputation: 1690

I think you have misunderstood something about component structure.

Assume that your child is a component which generates button for your other components. In this hierarchy your child has to inform it's parent that it was pressed.

child -----> parent

export default class Child extends Component {
  return (
    <Button onPress={this.props.onPress}/>
  );
}

In your parent component use child component to generate a button for you. In this way, you can use child component in any other components as an independent button.

export default class Parent extends Component {
  constructor(props) {
    super(props);
    this.execChildFunct=this.execChildFunct.bind(this);
  }
  execChildFunct: function() {
    console.log('Managed!');
  }
  return (
    <Child onPress={this.execChildFunct}></Child>
  );
}

Upvotes: 4

Ashok R
Ashok R

Reputation: 20796

It is in reactjs.

    class Child extends React.Component {
      componentDidMount() {
        this.props.onRef(this)
      }
      componentWillUnmount() {
        this.props.onRef(null)
      }
      method() {
        console.log('do stuff')
      }
      render() {
        return <h1>Hello World!</h1>
      }
    }

    class EnhancedChild extends React.Component {
      render() {
        return <Child {...this.props} />
      }
    }
        
    class Parent extends React.Component {
      onClick = () => {
        this.child.method() // do stuff
      };
      render() {
        return (
          <div>
            <EnhancedChild onRef={ref => (this.child = ref)} />
            <button onClick={this.onClick}>Child.method()</button>
          </div>
        );
      }
    }

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

Original Solution:

https://jsfiddle.net/frenzzy/z9c46qtv/

https://github.com/kriasoft/react-starter-kit/issues/909

Upvotes: 8

Himanshu padia
Himanshu padia

Reputation: 7730

Simple and easy way to Parent --> Child function call

    /* Parent.js */
    import React, { Component } from "react";
    import { TouchableOpacity, Text } from "react-native";
    import Child from "./Child";
    
    class Parent extends React.Component {
      onChildClick = () => {
        this.child.childFunction(); // do stuff
      };
      render() {
        return (
          <div>
            <Child onRef={(ref) => (this.child = ref)} />
            <TouchableOpacity onClick={this.onChildClick}>
              <Text>Child</Text>
            </TouchableOpacity>
          </div>
        );
      }
    }
    /* Child.js */
    import React, { Component } from "react";
    
    class Child extends React.Component {
      componentDidMount() {
        this.props.onRef(this);
      }
      componentWillUnmount() {
        this.props.onRef(undefined);
      }
      childFunction() {
        // do stuff
        alert("childFunction called");
      }
      render() {
        return <View>Hello World!</View>;
      }
    }

Original Solution: https://github.com/kriasoft/react-starter-kit/issues/909

Upvotes: 7

Adonis Gaitatzis
Adonis Gaitatzis

Reputation: 3739

Here's how you can do this with functional components:

Parent

  1. Use useRef() to give the child component a reference in the parent:
const childRef = useRef()
// ...
return (
   <ChildComponent ref={childRef} />
)
...

Child

  1. Pass ref as one of the constructor parameters:
const ChildComponent = (props, ref) => {
  // ...
}
  1. Import useImperativeHandle and forwardRef methods from the 'react' library:
import React, { useImperativeHandle, forwardRef } from 'react'
  1. Use useImperativeHandle to bind functions to the ref object, which will make these functions accessible to the parent

These methods won't be internally available, so you may want to use them to call internal methods.

const ChildComponent = (props, ref) => {
  //...
  useImperativeHandle(ref, () => ({
    // each key is connected to `ref` as a method name
    // they can execute code directly, or call a local method
    method1: () => { localMethod1() },
    method2: () => { console.log("Remote method 2 executed") }
  }))
  //...
  
  // These are local methods, they are not seen by `ref`,
  const localMethod1 = () => {
    console.log("Method 1 executed")
  }
  // ..
}
  1. Export the child component using forwardRef:
const ChildComponent = (props, ref) => {
  // ...
}
export default forwardRef(ChildComponent)

Putting it all together

Child Component

import React, { useImperativeHandle, forwardRef } from 'react';
import { View } from 'react-native'


const ChildComponent = (props, ref) => {
  useImperativeHandle(ref, () => ({
    // methods connected to `ref`
    sayHi: () => { sayHi() }
  }))
  // internal method
  const sayHi = () => {
    console.log("Hello")
  }
  return (
    <View />
  );
}

export default forwardRef(ChildComponent)

Parent Component

import React, { useRef } from 'react';
import { Button, View } from 'react-native';
import ChildComponent from './components/ChildComponent';

const App = () => {
  const childRef = useRef()
  return (
    <View>
      <ChildComponent ref={childRef} />
      <Button
        onPress={() => {
          childRef.current.sayHi()
        }}
        title="Execute Child Method"
      />
    </View>
  )
}

export default App

There is an interactive demo of this on Expo Snacks: https://snack.expo.dev/@backupbrain/calling-functions-from-other-components

This explanation is modified from this TutorialsPoint article

Upvotes: 39

Incinerator
Incinerator

Reputation: 2817

Nader Dabit's answer is outdated, since using String literals in ref attributes has been deprecated. This is how we would do it as of September 2017:

<Child ref={child => {this.child = child}} {...this.props} />
<Button onPress={this.child.myfunc} />

Same functionality, but instead of using a String to reference the component, we store it in a global variable instead.

Upvotes: 34

Nader Dabit
Nader Dabit

Reputation: 53711

You can add a ref to the child component:

<Child ref='child' {...this.props} />

Then call the method on the child like this:

<Button onPress={this.refs.child.myfunc} />

Upvotes: 26

Related Questions