Whats the difference between ForwardRefExoticComponent and ForwardRefRenderFunction in react?
Asked Answered
P

1

22

I'm writing a React component which can forward ref to its chilren

I found out that for the return type of the function components, I can use ForwardRefExoticComponent and ForwardRefRenderFunction. But I'm not sure whats the difference between them.

So far, When using ForwardRefExoticComponent, I can extend it while the ForwardRefRenderFunction cannot? I posted a question related to my case here : How to export forwardRef with ForwardRefRenderFunction

If anyone know whats the difference between them and what they do please help me. Because it seems that React team has no document about them (but they are inside react package)

Phototypography answered 7/10, 2020 at 5:35 Comment(0)
S
19

ForwardRefExoticComponent

The definition taken from here is

interface ExoticComponent<P = {}> {
    /**
     * **NOTE**: Exotic components are not callable.
     */
    (props: P): (ReactElement|null);
    readonly $$typeof: symbol;
}

interface NamedExoticComponent<P = {}> extends ExoticComponent<P> {
    displayName?: string;
}

interface ForwardRefExoticComponent<P> extends NamedExoticComponent<P> {
    defaultProps?: Partial<P>;
    propTypes?: WeakValidationMap<P>;
}

If you write it out you get

interface ForwardRefExoticComponent<P> {
    /**
     * **NOTE**: Exotic components are not callable.
     */
    (props: P): (ReactElement|null);
    readonly $$typeof: symbol;
    displayName?: string;
    defaultProps?: Partial<P>;
    propTypes?: WeakValidationMap<P>;
}

ForwardRefRenderFunction

The definition taken from here is

interface ForwardRefRenderFunction<T, P = {}> {
    (props: PropsWithChildren<P>, ref: ((instance: T | null) => void) | MutableRefObject<T | null> | null): ReactElement | null;
    displayName?: string;
    // explicit rejected with `never` required due to
    // https://github.com/microsoft/TypeScript/issues/36826
    /**
     * defaultProps are not supported on render functions
     */
    defaultProps?: never;
    /**
     * propTypes are not supported on render functions
     */
    propTypes?: never;
}

Differences

  • ForwardRefRenderFunction does not support propTypes and defaultProps, whereas ForwardRefExoticComponent does.
  • ForwardRefExoticComponent has an additional member $$typeof of type symbol
  • The call signature of ForwardRefRenderFunction takes a props object, which must include a member children and a ref object as parameters, whereas the call signature of ForwardRefExoticComponent only takes a props object of arbitrary shape as parameter.

Some more thoughts

The interplay of those two definitions is best seen in the definition of the forwardRef function:

function forwardRef<T, P = {}>(render: ForwardRefRenderFunction<T, P>): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;

Also, a big difference between the two definitions seems to be, that ForwardRefExoticComponent (like all exotic components) are no function components, but actually just objects, which are treated specially when rendering them. Therefore the comment

NOTE: Exotic components are not callable.

And for some reason, those exotic components are necessary in some places.

Swiftlet answered 7/10, 2020 at 7:19 Comment(3)
hi Peter, thanks for your answer. May I have some more of your recommendation. So is it a bad habit when using ForwardRefExoticComponent? Is it better if I just switch to class component instead because I think ref in class component will be more straightforward ?Phototypography
If you use forwardRef you must use ForwardRefExoticComponent. It is simply the return type of the function. But to avoid that, there are two ways: you can either switch to class components, where refs are enabled without anything to do as far as I can tell, or you can just define your ref as plain property of the component (which must not be called 'ref' - name it for example 'inputRef', 'compRef', etc.). This works totally fine as well. The only downside of this is, that you can't name your ref 'ref'.Swiftlet
Is must include a member children part still valid in 2022? I see the children prop in PropsWithChildren seems optional github.com/DefinitelyTyped/DefinitelyTyped/blob/…Wellfixed

© 2022 - 2024 — McMap. All rights reserved.