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

56 Views Asked by At

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
1

There are 1 best solutions below

0
Drew Reese On BEST ANSWER

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>
  );
}