Calling a children method of a functional component from parent

Viewed 1484

I am not using class, I'd like to learn how to do it manually. I am dealing with login screen. https://snack.expo.io/@ericsia/call-function-from-child-component If you want to show me your code, you need to save and share the link. So I wanted a functional component for display textbox (assume ChildComponent as the function name, so export ChildComponent).
So in Parent/Screen1 I have something like this right?

import * as React from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';

// You can import from local files
import ChildComponent from './components/ChildComponent';

export default function App() {
  
  function checkSuccess()
  {
    // call helloWorld from here
  }
  return (
    <View style={styles.container}>
   
        <ChildComponent />
    
        <TouchableOpacity style={styles.button}
        onPress={ checkSuccess } >
        <Text>helloWorld ChildComponent</Text>
      </TouchableOpacity>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
  button: {
    alignItems: "center",
    backgroundColor: "#DDDDDD",
    padding: 10
  },
});

so if the result is invalid right, I wanted to display a tiny red error message. something like this My approach is if I can call a function from the ChildComponent then I may still solve it.
I googled it and most of the solution provided is for class. I tried useEffect React.createRef useImperativeHandle but I didn't get it work.
for start, i am just trying to call the helloWorld()

import * as React from 'react';
import { TextInput , View, StyleSheet, Image } from 'react-native';

export default function ChildComponent() {
  function helloWorld()
  {
    alert("Hello World");
  }
  return (<TextInput placeholder="Try to call helloWorld from App.js"/>);
}

Another question, if I have a textbox in my ChildComponent how do I retrieve the text/value from parent?

1 Answers

The Easy Way: Passing Props

You can move the helloWorld function up to the parent component and pass it down to the child as a prop. That way both components can call it. I recommend using an arrow function when you are going to be passing around a function, though it doesn't matter in this case.

Parent

export default function App() {
  const helloWorld = () => {
    alert('Hello World');
  }
  const checkSuccess = () => {
    helloWorld();
  }

  return (
    <View style={styles.container}>
      <ChildComponent helloWorld={helloWorld} />
      <TouchableOpacity style={styles.button} onPress={checkSuccess}>
        <Text>helloWorld ChildComponent</Text>
      </TouchableOpacity>
    </View>
  );
}

Child

const ChildComponent = ({ helloWorld }) => {
  // could do anything with helloWorld here

  return <TextInput placeholder="Try to call helloWorld from App.js" />;
};

The Hard Way: Ref Forwarding

If you want to keep the function in the child component then you need to go through a lot of hoops. I do not recommend this approach.

You have to do all of these steps:

  • Create a ref object in the parent using useRef: const childRef = React.useRef();
  • Pass the ref to the child as a prop: <ChildComponent ref={childRef} />
  • Call the function on the current value of the child component ref, using ?. to avoid errors if .current has not yet been set: childRef.current?.helloWorld();
  • Accept the ref prop in the child by using forwardRef: React.forwardRef( (props, ref) => {
  • Expose the helloWorld function as an instance variable of the child component by using useImperativeHandle: React.useImperativeHandle(ref , () => ({helloWorld}));

Parent:

export default function App() {
  const childRef = React.useRef();

  function checkSuccess() {
    childRef.current?.helloWorld();
  }

  return (
    <View style={styles.container}>
      <ChildComponent ref={childRef} />
      <TouchableOpacity style={styles.button} onPress={checkSuccess}>
        <Text>helloWorld ChildComponent</Text>
      </TouchableOpacity>
    </View>
  );
}

Child:

const ChildComponent = React.forwardRef((props, ref) => {
  function helloWorld() {
    alert('Hello World');
  }

  React.useImperativeHandle(ref, () => ({ helloWorld }));

  return <TextInput placeholder="Try to call helloWorld from App.js" />;
});

Edit: Expo Link

Related