Search code examples
reactjsreact-forwardref

about forwardRef , I can't understand the second use case


Case I, I can accept those code as the picture shows, we define the ref in parent component and pass it to child component

App.js

import { useRef } from 'react';
import MyInput from './MyInput.js';

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

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <MyInput label="Enter your name:" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

MyInput.js

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  const { label, ...otherProps } = props;
  return (
    <label>
      {label}
      <input {...otherProps} ref={ref} />
    </label>
  );
});

export default MyInput;

Case II,the component Input was defined as following ,

import * as React from 'react'

import { cn } from '@/lib/utils'

export interface InputProps
  extends React.InputHTMLAttributes<HTMLInputElement> {}

const Input = React.forwardRef<HTMLInputElement, InputProps>(
  ({ className, type, ...props }, ref) => {
    return (
      <input
        type={type}
        className={cn(
          'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
          className
        )}
        ref={ref}
        {...props}
      />
    )
  }
)
Input.displayName = 'Input'

export { Input }

this was used in another file chat.js, the episoid as follwing

import { Input } from './ui/input'

<Input
        value={previewTokenInput}
        placeholder="OpenAI API key"
        onChange={e => setPreviewTokenInput(e.target.value)}
/>

something confused me , the parent didn't define Ref variable, and use directly . Is this a new approach of using forwardRef ?

the codes are from https://github.com/vercel-labs/ai-chatbot,

  • /component/chat.tsx
  • /component/ui/input.tsx

Solution

  • Something confused me, the parent didn't define Ref variable, and use directly.

    Just because a child component forwards a React ref doesn't mean a parent component necessarily needs to pass one. React components generally ignore props they don't care about, and for those props that are not passed their values will simply be undefined or receive a default/fallback value if that is how the child component was implemented.

    Is this a new approach of using forwardRef?

    No, this is always how the React.forwardRef function worked.

    There is no difference between the two components in terms of "cases".

    import { forwardRef } from 'react';
    
    const MyInput = forwardRef(function MyInput(props, ref) { // ref forwarded
      const { label, ...otherProps } = props;
      return (
        <label>
          {label}
          <input
            {...otherProps}
            ref={ref} // passing ref through to HTML element
          />
        </label>
      );
    });
    
    export default MyInput;
    
    const Input = React.forwardRef<HTMLInputElement, InputProps>( // ref forwarded
      ({ className, type, ...props }, ref) => {
        return (
          <input
            type={type}
            className={cn(
              '....',
              className
            )}
            ref={ref} // passing ref through to HTML element
            {...props}
          />
        )
      }
    )
    

    In terms of forwarding React refs, the "cases" are identical.

    What is different between the two though is in the first example, the code is actually creating a ref and passing it to the MyInput component.

    import { useRef } from 'react';
    import MyInput from './MyInput.js';
    
    export default function Form() {
      const ref = useRef(null); // <-- created ref
    
      function handleClick() {
        ref.current.focus();
      }
    
      return (
        <form>
          <MyInput label="Enter your name:" ref={ref} /> // <-- passed ref
          <button type="button" onClick={handleClick}>
            Edit
          </button>
        </form>
      );
    }
    

    The same could be done with the second code example:

    import { useRef } from 'react';
    import { Input } from './ui/input';
    
    export default function Form() {
      const ref = useRef(null);
    
      function handleClick() {
        ref.current.focus();
      }
    
      return (
        <form>
          <Input
            value={previewTokenInput}
            placeholder="OpenAI API key"
            onChange={e => setPreviewTokenInput(e.target.value)}
            ref={ref}
          />
          <button type="button" onClick={handleClick}>
            ....
          </button>
        </form>
      );
    }