React Tips — Render Props and Forward Refs

John Au-Yeung - Jan 23 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

React is the most used front end library for building modern, interactive front end web apps. It can also be used to build mobile apps.

In this article, we’ll look at how to use render props to create a component that renders things flexibly and passing refs to a child with forwarding refs.

Render Props

Render prop is a pattern where we pass in the render function as a prop. To use the pattern, we create a component that calls the render function from props. Then from another component, we pass in the render prop with the function as the value to the component which calls render .

For instance, we can write the following code:

import React from "react";

const RenderComponent = ({ render }) => {
  const [foo] = React.useState("foo");
  const [bar] = React.useState("bar");

  return render({
    foo,
    bar
  });
};

export default function App() {
  return (
    <>
      <RenderComponent
        render={({ foo, bar }) => (
          <p>
            {foo} {bar}
          </p>
        )}
      />
      <RenderComponent
        render={({ foo, bar }) => (
          <p style={{ color: "green" }}>
            {foo} {bar}
          </p>
        )}
      />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we have the RenderComponent , which calls the render function passed in from props. It just calls the render function that's passed in from props with the states inside RenderComponent .

Then in App , we referenced RenderComponent twice, with the render prop set the different functions for each.

Therefore, we should see ‘foo bar’ displayed twice, with the 2nd one being green.

This is useful for formatting data differently from the same state without repeating code.

Forward Refs

Forwarding refs means that we are getting the refs from a child component from a parent component.

For instance, if we want to get the ref of a button that resides in a custom button component, we can write the following code:

import React, { useEffect } from "react";

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref}>{props.children}</button>
));

export default function App() {
  const buttonRef = React.createRef();

  useEffect(() => {
    buttonRef.current.focus();
  }, []);

  return (
    <>
      <FancyButton ref={buttonRef}>Click me!</FancyButton>;
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we have the FancyButton component, which has the button element, which we want to access. To let us access it from App or another component, we call React.forwardRef , which takes a callback with 2 parameters, props and ref .

The props have the props, and the ref is the ref that we can access from the outside. We set the ref prop to the ref parameter so to set the ref to the button.

Then in App , we create the buttonRef and pass it into ref . Then in the useEffect callback, we call buttonRef.current.focus(); to focus the button when App load since we have an empty array as the 2nd argument.

Forwarding refs are also used with 3rd party components. For instance, if we have to use the react-hook-form package and we’re using our own custom input control components, then we have to call forwardRef as follows:

import React from "react";
import { useForm } from "react-hook-form";

const Select = React.forwardRef(({ label }, ref) => (
  <>
    <label>{label}</label>
    <select name={label} ref={ref}>
      <option value="10">10</option>
      <option value="20">20</option>
    </select>
  </>
));

export default function App() {
  const { register, handleSubmit } = useForm();

  const onSubmit = () => {};

  return (
    <>
      <form onSubmit={handleSubmit(onSubmit)}>
        <Select label="Age" ref={register} />
        <input type="submit" />
      </form>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we created a custom select control called Select , which is created by calling forwardRef with a callback that takes the ref as the 2nd parameter, we set it as the value of the ref prop in the select element.

Then we can access the ref of select from App .

Conclusion

Render props is the pattern where we pass in the render function as a prop into a child component. The child component has the states that are rendered by the function we passed into the render prop.

Then we can reference that child component multiple times with different render functions.

Forwarding refs let us access the ref of an element from a child component from within the parent. This is useful for custom components that are used for customizing an existing HTML element.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .