How to export forwardRef with ForwardRefRenderFunction
Asked Answered
O

2

3

I'm having a component which belongs to a UI library, lets call it Input component. When using this library to call Input, there are many types I can called. For example

<Input />
<Input.TextArea />
<Input.Search />

Now I want to write a wrapper to this Input component, so I write like this

type InputWrapperComponent = FC<InputProps> & {
  Search: typeof Input.Search;
  TextArea: typeof Input.TextArea;
};

const InputWrapper: InputWrapperComponent = (props) => {
  // make some minor changes here
}

InputWrapper.Search = Input.Search;
InputWrapper.TextArea = Input.TextArea;
export default InputWrapper;

in index.tsx

export { default as InputWrapper } from './input';

Then I can use them like this:

<InputWrapper />. --> Works
<InputWrapper.TextArea />. --> Works
<InputWrapper.Search />. --> Works

But by doing this, I cannot use ref methods of the original UI library ( something like inputRef.current.focus() ). that's why I use forwardRef and ForwardRefRenderFunction like this

type InputWrapperComponent = ForwardRefRenderFunction<HTMLInputElement, InputProps> & {
  Search: typeof Input.Search;
  TextArea: typeof Input.TextArea;
};

const InputWrapper: InputWrapperComponent = (props, ref) => {
  // make some minor changes here and add the ref to the input
}

InputWrapper.Search = Input.Search;
InputWrapper.TextArea = Input.TextArea;
export default forwardRef(InputWrapper);

By changing to this, I can pass the ref to the original UI library and can use its original methods. However, my problem right now is when I change to forwardRef and ForwardRefRenderFunction, I cannot call TextArea and Search anymore

<InputWrapper />. --> Works
<InputWrapper.TextArea />. --> Error
<InputWrapper.Search />. --> Error

This is the error:

Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Anyone can give me some guidance on this? Thanks

Oxidize answered 7/10, 2020 at 3:42 Comment(0)
S
1

What you are doing is:

  1. Define function InputWrapper as type InputWrapperComponent
  2. Define Search and TextArea as members of InputWrapper
  3. Create a ref forwarding component with forwardRef

The problem is, that forwardRef creates a completely new component and does not care, whether there have been some sub-components defined before. They will just be thrown away and you get the error of those members being undefined.

So the way to go is actually:

  1. Define function InputWrapper as type InputWrapperComponent
  2. Create a ref forwarding component with forwardRef
  3. Define Search and TextArea as members of the new ref forwarding component
Singband answered 7/10, 2020 at 7:23 Comment(1)
hi Peter, thanks for your answer. I wanna clarify, so based on your suggestion InputWrapperComponent will be defined as ForwardRefRenderFunction (step1). Then in the new ref forwarding component (step2) I have to declare it as ForwardRefExoticComponent right? As I'm using typescript so the eslint require the type of themOxidize
O
2

I end up using ForwardRefExoticComponent instead of ForwardRefRenderFunction

type InputWrapperComponent = React.ForwardRefExoticComponent<
  React.PropsWithoutRef<InputProps> & React.RefAttributes<HTMLInputElement>
> & {
  Search: typeof Input.Search;
  TextArea: typeof Input.TextArea;
  Password: typeof Input.Password;
};

But I really not sure whats the difference between them

Update: Please check Peter's answer.

For what is the the difference between the two, please check his answer here : Whats the difference between ForwardRefExoticComponent and ForwardRefRenderFunction in react?

Oxidize answered 7/10, 2020 at 5:32 Comment(0)
S
1

What you are doing is:

  1. Define function InputWrapper as type InputWrapperComponent
  2. Define Search and TextArea as members of InputWrapper
  3. Create a ref forwarding component with forwardRef

The problem is, that forwardRef creates a completely new component and does not care, whether there have been some sub-components defined before. They will just be thrown away and you get the error of those members being undefined.

So the way to go is actually:

  1. Define function InputWrapper as type InputWrapperComponent
  2. Create a ref forwarding component with forwardRef
  3. Define Search and TextArea as members of the new ref forwarding component
Singband answered 7/10, 2020 at 7:23 Comment(1)
hi Peter, thanks for your answer. I wanna clarify, so based on your suggestion InputWrapperComponent will be defined as ForwardRefRenderFunction (step1). Then in the new ref forwarding component (step2) I have to declare it as ForwardRefExoticComponent right? As I'm using typescript so the eslint require the type of themOxidize

© 2022 - 2024 — McMap. All rights reserved.