Surendhar Reddy

Forward refs in functional components

First, this is a continuation of my previous post on understanding how forward refs work in React/React Native. The piece focuses on making sense of forwarding refs with hooks.

The implementation of refs with hooks is similar to stateful component except that we a useRef hook to create a ref and attach it to the element. Below is a reasonably simple implementation of a simple list component.

import React from "react";
import { FlatList } from "react-native";

const SimpleList = () => {
  const simpleList = React.useRef();
  return <FlatList ref={simpleList} />;
};

In the component, we can access methods such as scrollToEnd with simpleList.current.scrollToEnd(), and it’s the same for the rest.

How it looks when the list is componentized? It’s again similar to the implementation that we do in stateful components.

import React from "react";
import { FlatList } from "react-native";

const AwesomeList = ({ forwardRef }) => {
  return <FlatList scrollsToTop bounces={false} ref={forwardRef} />;
};

export default React.forwardRef((props, ref) => (
  <AwesomeList {...props} forwardRef={ref} />
));

We have a functional component with forwarding refs functionality.

import React from "react";
import { FlatList } from "react-native";

import AwesomeList from "./AwesomeList";

const ListView = () => {
  const listView = React.useRef();
  return <AwesomeList ref={listView} />;
};

As you might have guessed, we can now access child component methods using the forwarded ref, for example, listView.current.scrollToEnd() and so on. Sounds good?


Oh wait, that’s not the end. Hooks also allow you to customize these methods that are exposed. Yes, it’s by using useImperativeHandle.

From the documentation;

useImperativeHandle customizes the instance value that is exposed to parent components when using ref. useImperativeHandle should be used with forwardRef.

How do we use it?

import React, { useImperativeHandle } from "react";
import { FlatList } from "react-native";

const AwesomeList = ({ forwardRef }) => {
  const simpleList = React.useRef();

  useImperativeHandle(forwardRef, () => ({
    customScrollToEnd: () => {
      if (simpleList.current) return simpleList.current.scrollToEnd();

      return null;
    },
  }));

  return <FlatList scrollsToTop bounces={false} ref={simpleList} />;
};

export default React.forwardRef((props, ref) => (
  <AwesomeList {...props} forwardRef={ref} />
));

In the above example, we create a ref and attach it to the element and expose those methods using useImperativeHandle , and in this case, we are exposing the list’s scrollToEnd functionality as customScrollToEnd which can be triggered in the parent component with listView.current.customScrollToEnd() method.