Problem

If you don't want to expose the DOM node but instead want to expose a facade of functions (that manipulate the DOM).

 

// App.js

import { useRef } from 'react';

import MyInput from './MyInput.js';


export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
    // This won't work because the DOM node isn't exposed:
    // ref.current.style.opacity = 0.5;
  }

  return (
    <form>
      <MyInput placeholder="Enter your name" ref={ref} />

      <button type="button" onClick={handleClick}>
        Edit
      </button>

    </form>
  );
}

 

// MyInput

import { forwardRef, useRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  const inputRef = useRef(null);

  useImperativeHandle(ref, () => {
    return {

      focus() {
        inputRef.current.focus();
      },

      scrollIntoView() {
        inputRef.current.scrollIntoView();
      },

    };
  }, []);

  return <input {...props} ref={inputRef} />;
});

export default MyInput;