How to call a React Child Function

Last update: 2023-10-31
Type
Quick Win's logo Quick Win
Membership
🆓
Tech
React React
React
TypeScript TypeScript
TypeScript
Share:

If you want to call a function of a child component from a parent component in React, you can use the useImperativeHandle hook.

Let’s say we have a simple Listing component that renders a random todo item from the JSONPlaceholder API.

import { View, Text, StyleSheet } from 'react-native';
import React, { useEffect, useState } from 'react';

type Listing = {
	userId: number;
	id: number;
	title: string;
	completed: boolean;
};

const Listing = () => {
	const [data, setData] = useState<Listing>();

	const loadData = async () => {
		const id = Math.floor(Math.random() * 200) + 1;
		const data = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`);
		const json = await data.json();
		setData(json);
	};

	useEffect(() => {
		loadData();
	}, []);

	return (
		<View style={styles.container}>
			{data ? (
				<Text style={{ fontSize: 20, color: '#fff' }}>{data.title}</Text>
			) : (
				<Text style={{ fontSize: 20, color: '#fff' }}>Loading...</Text>
			)}
		</View>
	);
};

const styles = StyleSheet.create({
	container: {
		backgroundColor: '#101353',
		alignItems: 'center',
		padding: 20,
		margin: 20
	}
});

export default Listing;

Now we want to call the loadData function from the parent component - but how?

The easiest way is to use the useImperativeHandle hook. It allows you to expose certain functions to the parent component.

To use it, we need to wrap our component in a forwardRef call and pass the ref to the useImperativeHandle hook.

import { View, Text, StyleSheet } from 'react-native';
import React, { useEffect, useState } from 'react';
import { forwardRef, useImperativeHandle } from 'react';

type Listing = {
	userId: number;
	id: number;
	title: string;
	completed: boolean;
};

interface Props {}

// Define the ref type
export type ListingRef = {
	refresh: () => void;
};

// Wrap the component in a forwardRef call
const ListingGood = forwardRef<ListingRef, Props>((props, ref) => {
	const [data, setData] = useState<Listing>();

	const fetchData = async () => {
		...
	};

	// Pass the ref to the useImperativeHandle hook
	useImperativeHandle(ref, () => ({
		refresh: () => {
			fetchData();
		}
	}));

	return (
		...
	);
});

const styles = StyleSheet.create({
	container: {
		backgroundColor: '#ff00ff',
		alignItems: 'center',
		padding: 20
	}
});

export default ListingGood;

Within the useImperativeHandle hook we can then define the functions that we want to expose to the parent component.

Now our parent component only needs to create a ref and pass it to the child component:

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

const Page = () => {
	// Create the ref
	const ref = useRef<ListingRef>(null);

	// Use the ref
	const updateMyChild = () => {
		ref.current?.refresh();
	};

	return (
		<View>
			<Button onPress={updateMyChild} title="Update child component" />
			{/* Add the ref */}
			<ListingGood ref={ref} />
		</View>
	);
};

export default Page;

Check out the useImperativeHandle docs for more information as well!

Simon Grimm